Skip to content

Commit

Permalink
Remove Send from Future/Stream
Browse files Browse the repository at this point in the history
This bound existed for two primary reasons, both detail below, and both of which
have now been solved.

One of the primary reasons this existed was due to the presence of `tailcall`.
Each standard combinator will call `tailcall` as appropriate, storing the
resulting trait object. Storing trait objects influences the applicatoin of the
`Send` and `Sync` bounds normally, but a key insight here is that we're not
storing trait objects but rather just pieces of otherwise internal futures.

With this insight the main storage for these futures, `Collapsed`, could simply
implement `Send` so long as the future itself originally implemented `Send`.
This in turn means that `tailcall` must be an `unsafe` method, but it seems well
worth the benefit of relaxing the `Send` bound.

The second primary reason for this bound was so the `Task` itself could be send.
This is critical for ensuring that futures can receive notifications from
multiple threads (e.g. be a future waiting on sources of multiple events).
Another key insight here, however, is that only the *outer* future needs to be
`Send`. We already have a solution, with `LoopData`, to make non-`Send` data
`Send`. By implementing `Future` directly for `LoopData<F: Future>`, this means
that it's trivial to make any future sendable by simply pinning it to an event
loop!

With these two pieces combined, it means that `Send` is no longer needed as a
bound on the `Future` and `Stream` traits. It may practically mean that
`LoopData` is used commonly in some scenarios, but that's quite a small price to
pay for relaxing the requirements of the core trait.

Some other ramifications of this change are:

* The `Future::boxed` and `Stream::boxed` methods now require `Self` to adhere
  to `Send`. This is expected to be the most common case, and in the less common
  case of not-`Send` `Box::new` can be used.
* Two new type aliases, `BoxFuture` and `BoxStream` have been introduced to
  assist in writing APIs that return a trait object which is `Send`. Both of
  these type aliases package in the `Send` bound.
* A new `LoopPin` type, added in the previous commit, can be used to easily
  generate handles that can be used to pin futures to an event loop without
  having a literal reference to the event loop itself.
  • Loading branch information
alexcrichton committed Aug 12, 2016
1 parent 250b115 commit 4d4ce22
Show file tree
Hide file tree
Showing 55 changed files with 420 additions and 411 deletions.
88 changes: 35 additions & 53 deletions FAQ.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
# FAQ

A collection of some commonly asked questions, with responses! If you find any
of these unsatisfactory feel free to ping me (@alexcrichton) on github,c
of these unsatisfactory feel free to ping me (@alexcrichton) on github,
acrichto on IRC, or just by email!

### Why `Send + 'static`?

The `Future` trait and all of its associated items require `Send + 'static`.
This expressed the constraint that all futures must be sendable across threads
as well as not contain any borrowed data. A common question though is why not
just let this fall out of the types themselves? That is, I'll have `Send +
'static` futures if they happend to contain `Send + 'static` data.

On a technical level this is not currently possible. Due to the `tailcall`
method which flattens a chain of futures, futures commonly may store trait
objects. As trait objects must decide on `Send` and `'static` early on, we opted
to say "yes, futures will be both" early on.

Doesn't this impose an extra cost though? Other libraries only require `'static`
which allows one to use types like `Rc` and `RefCell` liberally. This is true
that futures themselves cannot contain data like an `Rc`, but instead if using
an event loop you can use an abstraction like `LoopData<T>` which allows storing
a non-`Send` piece of data but the `LoopData<T>` sentinel itself is sendable.
This can then be stored in futures and futures can negotiate which thread
they're polled on to acquire the data.

The final reason is that almost all futures end up being `Send + 'static` in
practice. This allows for a convenient implementation of driving futures by
simply polling a future on whichever thread originates an event, ensuring a
prompt resolution of a future if one is available. This, when combined with the
technical difficulties and ergonomic concerns of *not* having `Send` and
`'static`, led to the conclusion that the trait will require both.
### Why `'static`?

The `Future` trait and all of its associated items require `'static`. This
expresses the constraint that all futures must not contain any borrowed data. A
common question though is why not just let this fall out of the types
themselves? That is, I'll have `'static` futures if they happend to contain
`'static` data.

At the fundamental level, futures respresent state machines which are pushed
forward across turns of an underlying event loop somewhere. References to
non-`'static` data are typically on the stack, but the stack is not persisted
across turns of this underlying event loop. That is, the only safe data for a
future to actually reference is typically data owned above the stack frame of
the event loop itself. Event loops typically are created near the top of a
program, though, so there's typically not a whole lot of data that would be
safely store-able anyway!

For now, though, we believe that `'static` is a rough approximation for "data
owned above the event loop" and is the 99% use case of futures anyway. We're
interested in exploring alternatives though, to relax this constraint!

### Why both `Item` and `Error` associated types?

An alternative design of the `Future` trait would be to only have one associated
type, `Item`, and then most futures would resolve to `Result<T, E>`. The
intention of futures, the fundamental support for async I/O, typically means
that errors will be encoded in almost all futures anyway though. By encoding an
error type in the future as well we're able to provide convenient combinators
like `and_then` which automatically propagate errors, as well as combinators
like `join` which can act differently depending on whether a future resolves to
an error or not.

### Do futures work with multiple event loops?

Expand Down Expand Up @@ -85,28 +89,6 @@ fixes that make compilation much speedier.

### Is it on crates.io?

Not yet! A few names are reserved, but they're not functional. I'd use the git
repository here for now.

### Why both `then` and `and_then`?

TBA (to-be-answered)



















Not yet! A few names are reserved, but crates cannot have dependencies from a
git repository. Right now we depend on the master branch of `mio`, and crates
will be published once that's on crates.io as well!
91 changes: 44 additions & 47 deletions TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,9 @@ The core trait of the [`futures`] crate is [`Future`]. This trait represents an
asynchronous computation which will eventually get resolved. Let's take a look:

```rust
trait Future: Send + 'static {
type Item: Send + 'static;
type Error: Send + 'static;
trait Future: 'static {
type Item: 'static;
type Error: 'static;

fn poll(&mut self, task: &mut Task) -> Poll<Self::Item, Self::Error>;
fn schedule(&mut self, task: &mut Task);
Expand All @@ -311,62 +311,53 @@ trait Future: Send + 'static {
I'm sure quite a few points jump out immediately about this definition, so
let's go through them all in detail!

* [`Send` and `'static`][send-and-static]
* [`'static`][static]
* [`Item` and `Error`][item-and-error]
* [`poll` and `schedule`][poll-and-schedule]
* [`Future` combinators][combinators]

### `Send` and `'static`
[send-and-static]: #send-and-static
### `'static`
[static]: #static

[Back to `Future`][future-trait]

```rust
trait Future: Send + 'static {
trait Future: 'static {
// ...
}
```

The first part of the `Future` trait you'll probably notice are the `Send +
'static` bounds. This is Rust's way of saying that a type can be sent to other
threads and also contains no stack references. This restriction on `Future` as
well as its associated types provides the guarantee that all futures, and their
results, can be sent to other threads.
The first part of the `Future` trait you'll probably notice is the `'static`
bounds. This is Rust's way of saying that a type contains no stack references.
This restriction on `Future` as well as its associated types provides the
guarantee that all futures, and their results, can be persisted beyond any stack
frame.

These bounds on the trait definition make futures maximally useful in
multithreaded scenarios - one of Rust's core strengths - with the
tradeoff that implementing futures for _non-`Send`_ data is not
straightforward (though it is possible). The `futures` crate makes
this tradeoff to empower consumers of futures, at the expense of
implementors of futures, as consumers are by far the more common case.
As a bonus, these bounds allow for some [interesting
optimizations][tailcall].
These bounds on the trait definition make futures maximally useful in most event
loops with the tradeoff that implementing futures for data that would otherwise
not be `'static` may require an allocation. This tradeoff is made mainly to
empower consumers of futures. More technical information about this
bounds can be found [in the FAQ][faq-why-static].

[tailcall]: http://alexcrichton.com/futures-rs/futures/trait.Future.html#method.tailcall

For more discussion on futures of non-`Send` data see the section on [event loop
data][event-loop-data]. Additionally, more technical information about these
bounds can be found [in the FAQ][faq-why-send].

[faq-why-send]: https://github.com/alexcrichton/futures-rs/blob/master/FAQ.md#why-send--static
[faq-why-static]: https://github.com/alexcrichton/futures-rs/blob/master/FAQ.md#why--static

### `Item` and `Error`
[item-and-error]: #send-and-static
[item-and-error]: #item-and-error

[Back to `Future`][future-trait]

```rust
type Item: Send + 'static;
type Error: Send + 'static;
type Item: 'static;
type Error: 'static;
```

The next aspect of the [`Future`] trait you'll probably notice is the two
associated types it contains. These represent the types of values that the
`Future` can resolve to. Each instance of `Future` can be thought of as
resolving to a `Result<Self::Item, Self::Error>`.

Each associated type, like the trait, is bound by `Send + 'static`, indicating
that they must be sendable to other threads and cannot contain stack references.
Each associated type, like the trait, is bound by `'static`, indicating that
they cannot contain stack references.

These two types will show up very frequently in `where` clauses when consuming
futures generically, and type signatures when futures are returned. For example
Expand Down Expand Up @@ -462,8 +453,8 @@ the relationship between [`schedule`] and [`Task`] is somewhat different than
that `poll` has.

Each [`Task`] can have a [`TaskHandle`] extracted from it via the
[`Task::handle`] method. This [`TaskHandle`] implements `Send + 'static` and has
one primary method, [`notify`]. This method, when called, indicates that a
[`Task::handle`] method. This [`TaskHandle`] implements `Send + 'static` and
has one primary method, [`notify`]. This method, when called, indicates that a
future can make progress, and may be able to resolve to a value.

[`Task::handle`]: http://alexcrichton.com/futures-rs/futures/struct.Task.html#method.handle
Expand Down Expand Up @@ -559,18 +550,18 @@ in the standard library:
Let's take a look at the [`Stream`] trait in the [`futures`] crate:

```rust
trait Stream: Send + 'static {
type Item: Send + 'static;
type Error: Send + 'static;
trait Stream: 'static {
type Item: 'static;
type Error: 'static;

fn poll(&mut self, task: &mut Task) -> Poll<Option<Self::Item>, Self::Error>;
fn schedule(&mut self, task: &mut Task);
}
```

You'll notice that the [`Stream`] trait is very similar to the [`Future`] trait.
It requires `Send + 'static`, has associated types for the item/error, and has a
`poll` and `schedule` method. The primary difference, however, is that a
You'll notice that the [`Stream`] trait is very similar to the [`Future`]
trait. It requires `'static`, has associated types for the item/error, and has
a `poll` and `schedule` method. The primary difference, however, is that a
stream's [`poll`][stream-poll] method returns `Option<Self::Item>` instead of
`Self::Item`.

Expand Down Expand Up @@ -821,7 +812,7 @@ other end. The [`Complete::complete`] method will transmit the value to the
receiving end.

The second half, `rx` ("receiver"), is of type [`Promise`][promise-type] which is
a type that implements the [`Future`] trait. The `Item` type is `T`, the type of
a type that implements the [`Future`] trait. The `Item` type is `T`, the type of
the promise.
The `Error` type is [`Canceled`], which happens when the [`Complete`] half is
dropped without completing the computation.
Expand Down Expand Up @@ -1065,13 +1056,13 @@ one piece of a larger asynchronous computation. This means that futures come
and go, but there could also be data that lives for the entire span of a
computation that many futures need access to.

Futures themselves require `Send + 'static`, so we have two choices to share
data between futures:
Futures themselves require `'static`, so we have two choices to share data
between futures:

* If the data is only ever used by one future at a time we can thread through
ownership of the data between each future.
* If the data needs to be accessed concurrently, however, then we'd have to
naively store data in an `Arc` or worse, in an `Arc<Mutex>` if we wanted
naively store data in an `Arc`/`Rc` or worse, in an `Arc<Mutex>` if we wanted
to mutate it.

But both of these solutions are relatively heavyweight, so let's see if we
Expand Down Expand Up @@ -1107,16 +1098,22 @@ accessing data from a task and it's primarily used in manual implementations of
[Back to top][top]

We've now seen that we can store data into a [`Task`] with [`TaskData`], but
this requires that the data inserted is still `Send`. Sometimes data is not
`Send` or otherwise needs to be persisted yet not tied to a [`Task`]. For this
purpose the [`futures-mio`] crate provides a similar abstraction, [`LoopData`].
data is sometimes not `Send` or otherwise needs to be persisted yet not tied to
a [`Task`]. For this purpose the [`futures-mio`] crate provides a similar
abstraction, [`LoopData`].

[`LoopData`]: http://alexcrichton.com/futures-rs/futures_mio/struct.LoopData.html

The [`LoopData`] is similar to [`TaskData`] where it's a handle to data
conceptually owned by the event loop. The key property of [`LoopData`], however,
is that it implements the `Send` trait regardless of the underlying data.

One key method on the [`Future`] trait, [`forget`], requires that the future is
`Send`. This is not always possible for a future itself, but a `LoopData<F:
Future>` is indeed `Send` and the `Future` trait is implemented directly for
`LoopData<F>`. This means that if a future is not `Send`, it can trivially be
made `Send` by "pinning" it to an event loop through `LoopData`.

A [`LoopData`] handle is a bit easier to access than a [`TaskData`], as you
don't need to get the data from a task. Instead you can simply attempt to access
the data with [`get`][loop-get] or [`get_mut`][loop-get-mut]. Both of these
Expand Down
9 changes: 4 additions & 5 deletions futures-cpupool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//!
//! use std::sync::mpsc::channel;
//!
//! use futures::{Future, Task};
//! use futures::Future;
//! use futures_cpupool::CpuPool;
//!
//! # fn long_running_computation() -> u32 { 2 }
Expand All @@ -32,10 +32,9 @@
//!
//! // Block the current thread to get the result.
//! let (tx, rx) = channel();
//! Task::new().run(c.then(move |res| {
//! tx.send(res).unwrap();
//! Ok(())
//! }).boxed());
//! c.then(move |res| {
//! tx.send(res)
//! }).forget();
//! let res = rx.recv().unwrap();
//!
//! // Print out the result
Expand Down
14 changes: 9 additions & 5 deletions futures-cpupool/tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ extern crate futures_cpupool;

use std::sync::mpsc::channel;

use futures::{Future, Task};
use futures::Future;
use futures_cpupool::CpuPool;

fn get<F: Future>(f: F) -> Result<F::Item, F::Error> {
fn get<F>(f: F) -> Result<F::Item, F::Error>
where F: Future + Send,
F::Item: Send,
F::Error: Send,
{
let (tx, rx) = channel();
Task::new().run(f.then(move |res| {
f.then(move |res| {
tx.send(res).unwrap();
Ok(())
}).boxed());
futures::finished::<(), ()>(())
}).forget();
rx.recv().unwrap()
}

Expand Down
2 changes: 1 addition & 1 deletion futures-curl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl Session {
///
/// This function returns a future which will resolve to a `Session` once
/// it's been initialized.
pub fn new(handle: LoopHandle) -> Box<IoFuture<Session>> {
pub fn new(handle: LoopHandle) -> IoFuture<Session> {
let handle2 = handle.clone();

// The core part of a `Session` is its `LoopData`, so we create that
Expand Down
8 changes: 4 additions & 4 deletions futures-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ extern crate log;
use std::io;
use std::ops::BitOr;

use futures::{Task, Future};
use futures::stream::Stream;
use futures::{Task, BoxFuture};
use futures::stream::{Stream, BoxStream};

/// A macro to assist with dealing with `io::Result<T>` types where the error
/// may have the type `WouldBlock`.
Expand All @@ -60,10 +60,10 @@ macro_rules! try_nb {
}

/// A convenience typedef around a `Future` whose error component is `io::Error`
pub type IoFuture<T> = Future<Item=T, Error=io::Error>;
pub type IoFuture<T> = BoxFuture<T, io::Error>;

/// A convenience typedef around a `Stream` whose error component is `io::Error`
pub type IoStream<T> = Stream<Item=T, Error=io::Error>;
pub type IoStream<T> = BoxStream<T, io::Error>;

mod impls;

Expand Down
4 changes: 2 additions & 2 deletions futures-io/src/read_exact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ enum State<A, T> {
/// the buffer.
pub fn read_exact<A, T>(a: A, buf: T) -> ReadExact<A, T>
where A: ReadTask,
T: AsMut<[u8]> + Send + 'static,
T: AsMut<[u8]> + 'static,
{
ReadExact {
state: State::Reading {
Expand All @@ -53,7 +53,7 @@ fn eof() -> io::Error {

impl<A, T> Future for ReadExact<A, T>
where A: ReadTask,
T: AsMut<[u8]> + Send + 'static,
T: AsMut<[u8]> + 'static,
{
type Item = (A, T);
type Error = io::Error;
Expand Down
Loading

0 comments on commit 4d4ce22

Please sign in to comment.