├── LICENSE ├── README.md ├── executors.md ├── futures-02.md └── task-context.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 rust-lang-nursery 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Futures RFCs 2 | 3 | Much like Rust's [RFC repo](https://github.com/rust-lang/rfcs), this repository houses RFCs related to futures. RFCs should follow the [Rust RFC template](https://github.com/rust-lang/rfcs/blob/master/0000-template.md) and be proposed via pull requests to this repository. 4 | 5 | Decisions will ultimately rest on consensus of the futures core team: 6 | 7 | - @alexcrichton 8 | - @cramertj 9 | - @aturon 10 | 11 | and will seek consensus from stakeholders as usual. 12 | -------------------------------------------------------------------------------- /executors.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | [summary]: #summary 3 | 4 | This RFC proposes a design for `futures-executors`, including both executor 5 | traits and built-in executors. In addition, it sets up a core expectation for all 6 | tasks that they are able to spawn additional tasks, while giving fine-grained 7 | control over what executor that spawning is routed to. 8 | 9 | NOTE: this RFC assumes that [RFC #2] is accepted. 10 | 11 | [RFC #2]: https://github.com/rust-lang-nursery/futures-rfcs/pull/2 12 | 13 | # Motivation 14 | [motivation]: #motivation 15 | 16 | This is a follow-up to the [Tokio Reform], [Futures 0.2], and [Task Context] 17 | RFCs, harmonizing their designs. 18 | 19 | [Tokio Reform]: https://github.com/tokio-rs/tokio-rfcs/pull/3 20 | [Futures 0.2]: https://github.com/rust-lang-nursery/futures-rfcs/pull/1 21 | [Task Context]: https://github.com/rust-lang-nursery/futures-rfcs/pull/2 22 | 23 | The design in this RFC has the following goals: 24 | 25 | - Add a core assumption that tasks are able to spawn additional tasks, avoiding 26 | the need to reflect this at the API level. (Note: this assumption is only made 27 | in contexts that also assume an allocator.) 28 | 29 | - Provide fine-grained control over which executor is used to fulfill that assumption. 30 | 31 | - Provide reasonable built-in executors for both single-threaded and 32 | multithreaded execution. 33 | 34 | It brings the futures library into line with the design principles of the [Tokio 35 | Reform], but also makes some improvements to the `current_thread` design. 36 | 37 | # Proposed design 38 | 39 | ## The core executor abstraction: `Executor` 40 | 41 | First, in `futures-core` we define a general purpose executor interface: 42 | 43 | ```rust 44 | pub trait Executor { 45 | fn spawn(&self, f: Box + Send>) -> Result<(), SpawnError>; 46 | 47 | /// Provides a best effort **hint** to whether or not `spawn` will succeed. 48 | /// 49 | /// This allows a caller to avoid creating the task if the call to `spawn` will fail. This is 50 | /// similar to `Sink::poll_ready`, but does not provide any notification when the state changes 51 | /// nor does it provide a **guarantee** of what `spawn` will do. 52 | fn status(&self) -> Result<(), SpawnError> { 53 | Ok(()) 54 | } 55 | 56 | // Note: also include hooks to support downcasting 57 | } 58 | 59 | // opaque struct 60 | pub struct SpawnError { .. } 61 | 62 | impl SpawnError { 63 | pub fn at_capacity() -> SpawnError { .. } 64 | pub fn is_at_capacity(&self) -> bool { .. } 65 | 66 | pub fn shutdown() -> SpawnError { .. } 67 | pub fn is_shutdown(&self) -> bool { .. } 68 | 69 | // ... 70 | } 71 | ``` 72 | 73 | The `Executor` trait is pretty straightforward (some alternatives and tradeoffs 74 | are discussed in the next section), and crucially is object-safe. Executors can 75 | refuse to spawn, though the default surface-level API glosses over that fact. 76 | 77 | We then build in an executor to the task context, stored internally as a trait 78 | object, which allows us to provide the following methods: 79 | 80 | ```rust 81 | impl task::Context { 82 | // A convenience for spawning onto the current default executor, 83 | // **panicking** if the executor fails to spawn 84 | fn spawn(&self, F) 85 | where F: Future + Send + 'static; 86 | 87 | // Get direct access to the default executor, which can be used 88 | // to deal with spawning failures 89 | fn executor(&self) -> &mut Executor; 90 | } 91 | ``` 92 | 93 | With those APIs in place, we've achieved two of our goals: it's possible for any 94 | future to spawn a new task, and to exert fine-grained control over generic task 95 | spawning within a sub-future. 96 | 97 | ## Built-in executors 98 | 99 | In the `futures-executor` crate, we then add two executors: `ThreadPool` and 100 | `LocalPool`, providing multi-threaded and single-threaded execution, 101 | respectively. 102 | 103 | ### Multi-threaded execution: `ThreadPool` 104 | 105 | The `ThreadPool` executor works basically like `CpuPool` today: 106 | 107 | ```rust 108 | struct ThreadPool { ... } 109 | impl Executor for ThreadPool { ... } 110 | 111 | impl ThreadPool { 112 | // sets up a pool with the default number of threads 113 | fn new() -> ThreadPool; 114 | } 115 | ``` 116 | 117 | Tasks spawn onto a `ThreadPool` will, by default, spawn any subtasks onto the 118 | same executor. 119 | 120 | ### Single-threaded execution: `LocalPool` 121 | 122 | The `LocalPool` API, on the other hand, is a bit more subtle. Here, we're 123 | replacing `current_thread` from [Tokio Reform] with a slightly different, more 124 | flexible design that integrates with the default executor system. 125 | 126 | First, we have the basic type definition and executor definition, which is much 127 | like `ThreadPool`: 128 | 129 | ```rust 130 | // Note: not `Send` or `Sync` 131 | struct LocalPool { ... } 132 | impl Executor for LocalPool { .. } 133 | 134 | impl LocalPool { 135 | // create a new single-threaded executor 136 | fn new() -> LocalPool; 137 | } 138 | ``` 139 | 140 | However, the rest of the API is more interesting: 141 | 142 | ```rust 143 | impl LocalPool { 144 | // runs the executor until `f` is resolved, spawning subtasks onto `exec` 145 | fn run_until(&self, f: F, exec: E) -> Result 146 | where F: Future, E: Executor; 147 | 148 | // a future that completes when the executor has completed all of its tasks 149 | fn all_done(&self) -> impl Future; 150 | 151 | // spawns a possibly non-Send future, possible due to single-threaded execution. 152 | fn spawn_local(&self, F) 153 | where F: Future; 154 | } 155 | ``` 156 | 157 | The `LocalPool` is always run until a particular future completes execution, and 158 | lets you *choose* where to spawn any subtasks. If you want something like 159 | `current_thread` from [Tokio Reform], you: 160 | 161 | - Use `all_done()` as the future to resolve, and 162 | - Use the `LocalPool` *itself* as the executor to spawn subtasks onto by default. 163 | 164 | On the other hand, if you are trying to run some futures-based code in a 165 | synchronous setting (where you'd use `wait` today), you might prefer to direct 166 | any spawned subtasks onto a `ThreadPool` instead. 167 | 168 | 169 | # Rationale, drawbacks and alternatives 170 | [alternatives]: #alternatives 171 | 172 | The core rationale here is that, much like the global event loop in [Tokio 173 | Reform], we'd like to provide a good "default executor" to all tasks. Using 174 | `task::Context`, we can provide this assumption ergonomically without using TLS, 175 | by essentially treating it as *task*-local data. 176 | 177 | The design here is unopinionated: it gives you all the tools you need to control 178 | the executor assumptions, but doesn't set up any particular preferred way to do 179 | so. This seems like the right stance for the core futures library; external 180 | frameworks (perhaps Tokio) can provide more opinionated defaults. 181 | 182 | This stance shows up particularly in the design of `LocalPool`, which differs 183 | from `current_thread` in providing flexibility about executor routing and 184 | completion. It's possible that this will re-open some of the footguns that 185 | `current_thread` was trying to avoid, but the explicit `exec` parameter and 186 | `all_done` future hopefully make things more clear. 187 | 188 | Unlike `current_thread`, the `LocalPool` design does not rely on TLS, instead 189 | requiring access to `LocalPool` in order to spawn. This reflects a belief that, 190 | especially with borrowng + async/await, most spawning should go through the 191 | default executor, which will usually be a thread pool. 192 | 193 | Finally, the `Executor` trait is more restrictive than the futures 0.1 executor 194 | design, since it is tied to boxed, sendable futures. That's necessary when 195 | trying to provide a *universal* executor assumption, which needs to use dynamic 196 | dispatch throughout. This assumption is avoided in `no_std` contexts, and in 197 | general one can of course use a custom executor when desired. 198 | 199 | # Unresolved questions 200 | [unresolved]: #unresolved-questions 201 | 202 | TBD 203 | -------------------------------------------------------------------------------- /futures-02.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | [summary]: #summary 3 | 4 | Prepare for a push toward futures 1.0 this year by releasing a futures 0.2 5 | that's structured for faster iteration. 6 | 7 | The main goals of this release are: 8 | 9 | - Separation of `futures` into multiple, smaller crates, which are reexported 10 | in a `futures` facade crate. This will make it possible to gradually evolve 11 | `futures` libraries without major ecosystem breakage. 12 | - A few substantial changes, potentially including [making task handling 13 | explicit]. 14 | - Emptying the queue of minor breaking changes, including removing deprecated 15 | features and minor API tweaks (renamings, generalizations, etc.). 16 | 17 | [making task handling explicit]: (https://github.com/rust-lang-nursery/futures-rfcs/pull/2) 18 | 19 | This breakage is intended to overlap with library changes resulting from the 20 | release of the `tokio` crate (formerly `tokio-core`) and `mio` 0.7. The `tokio` 21 | changes will result in changes to many popular libraries in the Rust async 22 | ecosystem. By releasing `futures` 0.2 at or around the same time, we hope to 23 | minimize the number of breaking changes to the ecosystem. 24 | 25 | # Motivation 26 | [motivation]: #motivation 27 | 28 | The futures team would like to iterate toward and publish a 1.0 in 2018. We 29 | aren't going to be able to do that in one big step, in part because we are 30 | anticipating further changes to work well with async/await (see [@withoutboats's 31 | blog series] for more on that). part of the motivation for this RFC is to kick 32 | off that iteration, and also make space for further iterations by breaking up 33 | the crates -- in particular, splitting up the combinators and the different core 34 | traits to allow for more independent revisions. 35 | 36 | [@withoutboats's blog series]: https://boats.gitlab.io/blog/post/2018-01-30-async-iii-moving-forward/ 37 | 38 | ## Crate Separation 39 | [motivation-separation]: #motivation-separation 40 | 41 | When first publishing new functionality in a library, it is common for APIs to 42 | change in response to user feedback. API inconsistencies, missing features, 43 | unexpected edge-case behavior, and potential user-experience improvements are 44 | often discovered through real-world testing and experimentation. 45 | 46 | Unfortunately, popular crates like `futures` appear as public dependencies of 47 | other libraries. Making a breaking change to anything in the `futures` crate 48 | breaks libraries which exposed their own `Future` types or took `Future` types 49 | as arguments. 50 | 51 | Separating the `futures` crate into multiple independently-versioned 52 | libraries will allow breaking changes to combinators or other less stable 53 | parts of the library. These changes can be made more frequently without the 54 | ecosystem-wide breakage that would result from breakage of the `Future` or 55 | `Stream` traits. 56 | 57 | For example, imagine that the `asyncprint` crate exposes a function called 58 | `asyncprint` which takes a type `F: Future`, 59 | completes it, and prints the result. Internally, the implementation of 60 | `asyncprint` uses `and_then`, but it doesn't expose the `AndThen` type 61 | publicly. 62 | 63 | Imagine `AndThen` gets a breaking change update makes it slightly easier to use 64 | or more flexible. If `Future` and `AndThen` are both defined in the same crate, 65 | `futures`, then `futures` now has a major version increase. The new crate's 66 | `Future` trait is incompatible with the old crate's `Future` trait, even though 67 | no changes were made to `Future`. In this case, `asyncprint` cannot update to 68 | the new version of `futures` without breaking users, since the new version of 69 | `Future` is incompatible with types that implemented the old `Future`. 70 | 71 | However, if `Future` is defined in a separate crate from `AndThen`, 72 | `asyncprint` can update to the new version and use the new `AndThen` without 73 | breaking users. 74 | 75 | This is the goal for breaking apart `futures` into separate crates: after this 76 | change, it will be possible to make breaking changes to parts of `futures` 77 | without causing major ecosystem breakage. 78 | 79 | **Note: this RFC proposes a more aggressive "sharding" of crates than we 80 | expect in 1.0, to make space for iteration**. The details are given below. 81 | 82 | ## Small, Breaking API Improvements 83 | [motivation-small-breakage]: #motivation-small-breakage 84 | 85 | 0.2 breakage would make it possible to resolve a [number of minor issues] that 86 | require breaking changes to fix, as well as to clear out a number of old 87 | deprecations and renamings. 88 | 89 | [number of minor issues]: https://github.com/alexcrichton/futures-rs/issues?utf8=%E2%9C%93&q=is%3Aissue+0.2 90 | 91 | ## Timing in Coordination with Tokio 0.1 and Mio 0.7 92 | [motivation-timing]: #motivation-timing 93 | 94 | There is an impending release of the `tokio` crate which will use `mio` 0.7. 95 | Transitioning to use these new APIs will already require some amount of library 96 | breakage or deprecation and replacement. Specifically, functions which took 97 | an explicit handle to a `tokio` reactor core will no longer need to do so. 98 | Functions which formerly accepted the old `tokio-core` handles will not be 99 | compatible with the new `mio` 0.7-based `tokio` crate. 100 | 101 | Releasing `futures` 0.2 at the same as `tokio` would consolidate the API 102 | transition period. 103 | 104 | # Guide-level explanation 105 | [guide-level-explanation]: #guide-level-explanation 106 | 107 | The `futures` crate is working toward 1.0 this year! To get there, we're going 108 | to need to go through a few iterations, including exploration of the space 109 | around async/await. 110 | 111 | To make these transitions as painless as we can, we're doing a couple things today: 112 | 113 | - We're releasing futures 0.2 *in tandem* with the new Tokio crate, which is 114 | already going to result in an ecosystem shift. 115 | 116 | - In this release, we split the `futures` crate into a number of smaller 117 | libraries to allow for easier API evolution and validation as we work toward 118 | 1.0. The actual 1.0 release will contain a smaller set of stable crates. 119 | 120 | Library authors should update to use the appropriate crates: 121 | 122 | - `futures-core` for the `Future`, `Stream`, `IntoFuture`, `IntoStream` traits 123 | and their dependencies. 124 | - `futures-io` which includes updated `AsyncRead` and `AsyncWrite` traits. 125 | - `futures-sink` for the `Sink` trait which is used for sending values 126 | asynchronously. 127 | - `futures-executor` for the `Executor` trait which allows spawning futures 128 | to run asynchronously. 129 | - `futures-channel` for channels and other synchronization primitives. 130 | - `futures-util` which offers a number of convenient combinator functions, 131 | some of which are included in `FutureExt` and `StreamExt` extension traits. 132 | 133 | By minimizing the number of `futures` crates in the public API of your library, 134 | you will be able to receive more frequent updates without causing breaking 135 | changes for your users. 136 | 137 | Application developers can either depend on individual crates or on the 138 | `futures` crate, which provides convenience reexports of many of the 139 | `futures-xxx` crates. Expect that this crate will publish more frequent 140 | semver-breaking changes, but that updating will not normally result in 141 | significant or widespread breakage. 142 | 143 | These crates offer easier-to-use and easier-to-evolve APIs than the futures 0.1 144 | series. 145 | 146 | # Reference-level explanation 147 | [reference-level-explanation]: #reference-level-explanation 148 | 149 | Below you will find a *partial* list of some of the breaking changes we hope to 150 | include in the new `futures` 0.2 release. Note that this list is not exact, 151 | and does not aim to be so: this list is merely intended to motivate and 152 | explain some of the desired changes to the `futures` create. Please leave 153 | comments about specific issues on [the `futures` repo](https://github.com/alexcrichton/futures-rs), 154 | as detailed discussion could easily overwhelm the RFC thread. 155 | 156 | ## Crate Separation 157 | 158 | The `futures` crate will be *temporarily* split into several different 159 | libraries; before reaching 1.0, we will reduce the crate count. 160 | 161 | ### `futures-core` 162 | 163 | `futures-core` will include `Future`, `Stream`, `IntoFuture` 164 | traits and their dependencies. This is a central crate that includes only 165 | essential items, designed to minimize future breakage. Combinator methods 166 | will be removed from the `Future` and `Stream` and moved to the 167 | `FutureExt` and `StreamExt` traits in the `futures-util` crate. `poll` 168 | will be the only remaining method on the `Future` and `Stream` traits. 169 | This separation allows combinator functions to evolve over time without 170 | breaking changes to the public API surface of async libraries. 171 | 172 | ### `futures-io` 173 | 174 | `futures-io` includes the updated `AsyncRead` and `AsyncWrite` traits. 175 | These traits were previously included in the `tokio-io` crate. 176 | However, `AsyncRead` and `AsyncWrite` don't have any dependencies on the `tokio` 177 | crate or the rest of the `tokio` stack, so they will be moved into the `futures` 178 | group of libraries. `futures-io` depends only on the `futures-core` crate 179 | and the `bytes` crate (for the [`Buf`] and [`BufMut`] traits). 180 | 181 | [`Buf`]: https://carllerche.github.io/bytes/bytes/trait.Buf.html 182 | [`BufMut]: https://carllerche.github.io/bytes/bytes/trait.BufMut.html 183 | 184 | ### `futures-sink` 185 | 186 | `futures-sink` includes the `Sink` trait, which allows values to be sent 187 | asynchronously. The `Sink` trait is excluded from `futures-core` because its 188 | design is much less stable than `Future` and `Stream`. There are several 189 | potential breaking changes to the `Sink` trait under consideration. 190 | 191 | This crate will depend only on `futures-core`. 192 | 193 | ### `futures-channel` 194 | 195 | `futures-channel` includes the `mpsc` and `oneshot` messaging primitives. 196 | This crate is a minimal addition on `futures-core` designed for low churn 197 | so that channels may appear in the public APIs of libraries. 198 | 199 | `futures-channel` will not include a dependency on the `futures-sink` crate 200 | because it is more volatile. In order to have channels implement the `Sink` 201 | trait, channels in `futures-channel` will expose `poll_send` methods which 202 | will be used to implement `Sink` inside of the `futures-sink` crate. 203 | 204 | This is the only crate that is not compatible with `no_std`, as it relies 205 | on synchronization primitives from `std`. 206 | 207 | ### `futures-util` 208 | 209 | `futures-util` contains a number of convenient combinator functions for 210 | `Future`, `Stream`, and `Sink`, including the `FutureExt`, `StreamExt`, 211 | and `SinkExt` extension traits. 212 | 213 | This crate depends on `futures-core`, `futures-io`, and `future-sink`. 214 | 215 | ### `futures-executor` 216 | 217 | `futures-executor` contains the `Executor` trait which allows spawning 218 | asynchronously tasks onto a task executor. Like `Sink`, this is a less 219 | mature API which hasn't seen as much practical use. There are several 220 | possible breaking changes to this API coming in the near future. 221 | 222 | This crate depends on `futures-core`. 223 | 224 | ### The plan for 1.0 225 | 226 | After we've had a chance to iterate on these separate crates and to fully 227 | integrate with [async/await notation], we will consolidate down to *three* final crates: 228 | 229 | - `futures`, for `Future`, `Stream`, `Sink`, `AsyncRead`, 230 | `AsyncWrite`, and all related combinators. 231 | 232 | - `futures-channel`, for channels and other synchronization primitives. 233 | 234 | - `futures-executor` for the `Executor` trait which allows spawning futures 235 | to run asynchronously, and some built-in executors. 236 | 237 | [async/await notation]: https://boats.gitlab.io/blog/post/2018-01-30-async-iii-moving-forward/ 238 | 239 | ## Small, Breaking API Improvements and Clarifications 240 | 241 | - `Task` will be renamed to `Waker`. This name more clearly reflects how 242 | the type is used. It is a handle used to awaken tasks-- it is not a `Task` 243 | itself. 244 | - The `wait` method will be removed in favor of a free-function-based `blocking` API. 245 | - The `Notify` trait will be simplified to not require explicit ID passing, while 246 | still supporting management of allocation. 247 | - `Async` variant `NotReady` will be renamed to `Pending`. 248 | - `AsyncRead` and `AsyncWrite` no longer have `io::Read` and `io::Write` 249 | bounds. Discussion on this can be found [in this Github issue.](https://github.com/tokio-rs/tokio-io/issues/83) 250 | - `AsyncRead::prepare_uninitialized_buffer` has been replaced with an 251 | `initializer` function similar to [the unstable one on `io::Read`.](https://doc.rust-lang.org/std/io/trait.Read.html#method.initializer) 252 | This resolves the issue of the `unsafe` on `prepare_uninitialized_buffer` 253 | being `unsafe` to implement, *not* `unsafe` to use 254 | (the more common, and arguably the correct meaning of `unsafe fn`). 255 | - `AsyncRead` and `AsyncWrite` now offer separate methods for non-vectorized and 256 | vectorized IO operations, with the vectorized version based on `IoVec`. This 257 | makes the API easier to understand, makes the traits object-safe, and removes 258 | the dependency on the `bytes` crate. The `Buf`-based methods can be recovered 259 | via extension traits, which also enables them to work on trait objects. 260 | - `AsyncRead/Write` `read_buf`, `write_buf`, and `shutdown` have been renamed 261 | to `poll_read`, `poll_write`, and `poll_close` for increased standardization. 262 | The name `shutdown` was replaced [to avoid confusion with TCP shutdown.](https://github.com/tokio-rs/tokio-io/issues/80) 263 | - The flexibility of [`unfold`](https://github.com/alexcrichton/futures-rs/issues/260) 264 | will be increased. 265 | - [`JoinAll`'s lifetime requirements will be loosened.](https://github.com/alexcrichton/futures-rs/issues/285) 266 | - [`filter` and `skip_while` will be made more consistent and flexible.](https://github.com/alexcrichton/futures-rs/issues/311) 267 | - [`filter_map` will be more flexible.](https://github.com/alexcrichton/futures-rs/issues/338) 268 | - [The default implementation of `Sink::close` will be removed.](https://github.com/alexcrichton/futures-rs/issues/413) 269 | - [The implementation of `Future>` for `Option>` 270 | will be removed and replaced with an `IntoFuture` implementation.](https://github.com/alexcrichton/futures-rs/issues/445) 271 | - [The order of type parameters in combinators and types will be standardized.](https://github.com/alexcrichton/futures-rs/issues/517) 272 | - [`Flush` accessors will return `Option` instead of `panic`ing.](https://github.com/alexcrichton/futures-rs/issues/706) 273 | - [`future::FutureResult` will be renamed to `future::Result`.](https://github.com/alexcrichton/futures-rs/issues/706) 274 | 275 | ## Documentation 276 | 277 | In parallel with the 0.2 release, the futures team will continue working on a 278 | book documenting futures, as well as a thorough revamp of the API documentation. 279 | 280 | # Drawbacks 281 | [drawbacks]: #drawbacks 282 | 283 | - Async-ecosystem-wide breakage. 284 | - Separate crates may make the `futures` API surface harder to understand. 285 | This should be alleviated by reexporting all of the functionality through 286 | the functionality through the master `futures` crate. 287 | 288 | # Rationale and alternatives 289 | [alternatives]: #alternatives 290 | 291 | - Keep a subset of the methods on `Future` and `Stream` in the core trait, 292 | rather than the extension trait. The problem with this idea is that it's 293 | difficult to draw the line as to exactly which methods are prone to 294 | breakage. Potentially all of them could be subject to breakage in order 295 | to provide more convenient error-handling functionality. Furthermore, 296 | keeping some of the methods in `Future` and some in `FutureExt` could 297 | cause confusion among users and make it hard to discover the extension 298 | methods. 299 | - Require `Async::Pending` to include a `WillWake` returned from a method on 300 | `Waker` . This would ensure that a `Waker` has been acquired. 301 | `WillWake` could be zero-sized in release mode, and in debug 302 | mode it could indicate what task it came from. This could help tracking 303 | down bugs tied to lost wakeups. However, it's not clear how this would 304 | be used with `select`-style futures which only return `Pending` when 305 | all of their child futures return `Pending`. Which `WillWake` should 306 | be passed on in this case? Really, you need a way to indicate that all 307 | of the child future `WillWake` the task, not just one. This scenario 308 | is one of the situations in which wakeups are most commonly lost, so 309 | a solution here would provide a significant advantage to this proposal. 310 | Suggestions and ideas are welcome! 311 | 312 | # Unresolved questions 313 | [unresolved]: #unresolved-questions 314 | 315 | - Going forwards, it'd be interesting to explore ways for `cargo` or 316 | `crates.io` to natively support multi-crate packages or bundles. 317 | Facade crates such as the `futures` crate proposed here are a good 318 | intermediate solution. 319 | -------------------------------------------------------------------------------- /task-context.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | [summary]: #summary 3 | 4 | Learnability, reasoning, debuggability and `no_std`-compatibility improvements achieved by 5 | making task wakeup handles into explicit arguments. 6 | 7 | # Motivation 8 | [motivation]: #motivation 9 | 10 | ## Learnability 11 | 12 | The `futures` task wakeup mechanism is [a regular source of confusion for new 13 | users][explicit task issue]. Looking at the signature of `Future::poll`, it's 14 | hard to tell how `Future`s work: 15 | 16 | ```rust 17 | fn poll(&mut self) -> Poll; 18 | ``` 19 | 20 | `poll` takes no arguments, and returns either `Ready`, `NotReady`, or an error. 21 | From the signature, it looks as though the only way to complete a `Future` is 22 | to continually call `poll` in a loop until it returns `Ready`. This is 23 | incorrect. 24 | 25 | Furthermore, when implementing `Future`, it's necessary to schedule a wakeup 26 | when you return `NotReady`, or else you might never be `poll`ed again. The 27 | trick to the API is that there is an implicit `Task` argument to `Future::poll` 28 | which is passed using thread-local storage. This `Task` argument must be 29 | `notify`d by the `Future` in order for the task to awaken and `poll` again. 30 | It's essential to remember to do this, and to ensure that the right schedulings 31 | occur on every code path. 32 | 33 | [explicit task issue]: https://github.com/alexcrichton/futures-rs/issues/129 34 | 35 | ## Reasoning about code 36 | 37 | The implicit `Task` argument makes it difficult to tell which functions 38 | aside from `poll` will use the `Task` handle to schedule a wakeup. That has a 39 | few implications. 40 | 41 | First, it's easy to accidentally call a function that will attempt to access 42 | the task outside of a task context. Doing so will result in a panic, but it 43 | would be better to detect this mistake statically. 44 | 45 | Second, and relatedly, it is hard to audit a piece of code for which calls 46 | might involve scheduling a wakeup--something that is critical to get right 47 | in order to avoid "lost wakeups", which are far harder to debug than an 48 | explicit panic. 49 | 50 | ## Debuggability 51 | 52 | Today's implicit TLS task system provides us with fewer "hooks" for adding debugging 53 | tools to the task system. Deadlocks and lost wakeups are some of the trickiest things 54 | to track down with futures code today, and it's possible that by using a "two stage" 55 | system like the one proposed in this RFC, we will have more room for adding debugging 56 | hooks to track the precise points at which queing for wakeup occurred, and so on. 57 | 58 | More generally, the explicit `Context` argument and distinct `Waker` type provide greater 59 | scope for extensibility of the task system. 60 | 61 | ## no_std 62 | 63 | While it's possible to use futures in `no_std` environments today, the approach 64 | is somewhat of a hack. Threading an explicit `&mut Context` argument makes no_std 65 | support entirely straightforward, which may also have implications for the futures 66 | library ultimately landing in libcore. 67 | 68 | # Guide-level explanation 69 | [guide-level-explanation]: #guide-level-explanation 70 | 71 | The `Future`, `Stream`, `AsyncRead`, and `AsyncWrite` traits have been modified 72 | to take explicit `task::Context` arguments. `task::Context` is a type which 73 | provides access to 74 | [task-locals](https://docs.rs/futures/*/futures/macro.task_local.html) and a `Waker`. 75 | 76 | In terms of the old `futures` task model, the `Waker` is analogous to the `Task` 77 | returned by [`task::current`][current], and the `wake` method is analogous to 78 | the old [`Task::notify`][notify]. 79 | 80 | # Reference-level explanation 81 | [reference-level-explanation]: #reference-level-explanation 82 | 83 | The `task::Context` interface looks like this: 84 | 85 | ```rust 86 | mod task { 87 | 88 | /// The context for the currently running task. 89 | pub struct Context { 90 | 91 | /// Returns a `Waker` which can be used to wake up the current task. 92 | pub fn waker(&self) -> Waker { ... } 93 | 94 | /// Runs the provided closure with the task-local pointed to by the 95 | /// provided `key`. 96 | /// 97 | /// `LocalKey`s are acquired using the `task_local` macro. 98 | pub fn with_local(&mut self, key: LocalKey, f: F) -> R 99 | where F: FnOnce(&mut T) -> R 100 | { 101 | ... 102 | } 103 | } 104 | 105 | /// A handle used to awaken a task. 106 | pub struct Waker { 107 | 108 | /// Wakes up the associated task. 109 | /// 110 | /// This signals that the futures associated with the task are ready to 111 | /// be `poll`ed again. 112 | pub fn wake(&self) { ... } 113 | } 114 | 115 | ... 116 | } 117 | ``` 118 | 119 | The modified traits will look as follows: 120 | 121 | ```rust 122 | pub trait Future { 123 | type Item; 124 | type Error; 125 | fn poll(&mut self, ctx: &mut Context) -> Poll; 126 | } 127 | 128 | pub trait Stream { 129 | type Item; 130 | type Error; 131 | fn poll_next(&mut self, ctx: &mut Context) -> Poll, Self::Error>; 132 | } 133 | 134 | pub trait AsyncRead { 135 | unsafe fn initializer(&self) -> Initializer { ... } 136 | 137 | fn poll_read(&mut self, buf: &mut [u8], ctx: &mut Context) 138 | -> Poll; 139 | 140 | fn poll_vectored_read(&mut self, vec: &mut [&mut IoVec], ctx: &mut Context) 141 | -> Poll { ... } 142 | } 143 | 144 | pub trait AsyncWrite { 145 | fn poll_write(&mut self, buf: &[u8], ctx: &mut Context) 146 | -> Poll; 147 | 148 | fn poll_vectored_write(&mut self, vec: &[&IoVec], ctx: &mut Context) 149 | -> Poll { ... } 150 | 151 | fn poll_close(&mut self, ctx: &mut Context) -> Poll<(), io::Error>; 152 | } 153 | ``` 154 | 155 | # Drawbacks 156 | [drawbacks]: #drawbacks 157 | 158 | The primary drawback here is an ergonomic hit, due to the need to explicitly thread through 159 | a `Context` argument. 160 | 161 | For combinator implementations, this hit is quite minor. 162 | 163 | For larger custom future implementations, it remains possible to use TLS internally to recover the existing ergonomics, if desired. 164 | 165 | Finally, and perhaps most importantly, the explicit argument is never seen by users who only use 166 | futures via combinators or async-await syntax, so the ergonomic impact to such users in minimal. 167 | 168 | 169 | # Rationale and alternatives 170 | [alternatives]: #alternatives 171 | 172 | The two-stage design (splitting `Context` and `Waker`) is useful both for efficial task-local storage, and to provide space for debugging "hooks" in later iterations of the library. 173 | 174 | # Unresolved questions 175 | [unresolved]: #unresolved-questions 176 | 177 | - Can we come up with a better name than `task::Context`? `Task` is 178 | confusing because it suggests that we're being passed the task, 179 | when really we're _in_ the task. `TaskContext` is roughly equivalent 180 | to `task::Context` but doesn't allow users to `use futures::task::Context;` 181 | 182 | - What is the best way to integrate the explicit `task::Context` with 183 | async/await generators? If generators are changed to accept arguments to 184 | `resume`, a pointer could be passed through that interface. Otherwise, it 185 | would be necessary to pass the pointer through TLS when calling into 186 | generators. 187 | 188 | - The `task::Context::with_local` function can now likely be replaced by 189 | `HashMap`-like entry and accessor APIs. What should the precise API 190 | surface for accessing task-locals look like? 191 | --------------------------------------------------------------------------------