diff --git a/text/0000-scoped-threads.md b/text/0000-scoped-threads.md index b172ba4a..8a95d52a 100644 --- a/text/0000-scoped-threads.md +++ b/text/0000-scoped-threads.md @@ -41,7 +41,7 @@ Scoped threads in [Crossbeam](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/i have matured through years of experience and today we have a design that feels solid enough to be promoted into the standard library. -See the [rationale-and-alternatives](#rationale-and-alternatives) section for more. +See the [Rationale and alternatives](#rationale-and-alternatives) section for more. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -146,46 +146,63 @@ thread::scope(|s| { # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -We add a single new type to the `std::thread` module: +We add two new types to the `std::thread` module: ```rust -struct Scope<'a> {} +struct Scope<'env> {} +struct ScopedJoinHandle<'scope, T> {} +``` + +Lifetime `'env` represents the environment outside the scope, while +`'scope` represents the scope itself. More precisely, everything +outside the scope outlives `'env` and `'scope` outlives everything +inside the scope. The lifetime relations are: + +``` +'variables_outside: 'env: 'scope: 'variables_inside ``` Next, we need the `scope()` and `spawn()` functions: ```rust -fn scope<'a, F, T>(f: F) -> Result +fn scope<'env, F, T>(f: F) -> Result where - F: FnOnce(&Scope<'a>) -> T; + F: FnOnce(&Scope<'env>) -> T; -impl<'a> Scope<'a> { - fn spawn(&self, f: F) -> JoinHandle +impl<'env> Scope<'env> { + fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T> where - F: FnOnce(&Scope<'a>) -> T + Send + 'a, - T: Send + 'a; + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env; } ``` -There's just one more thing that will make the API complete: The thread builder -needs to be able to spawn threads inside a scope. +That's the gist of scoped threads, really. + +Now we just need two more things to make the API complete. First, `ScopedJoinHandle` +is equivalent to `JoinHandle` but tied to the `'scope` lifetime, so it will have +the same methods. Second, the thread builder needs to be able to spawn threads +inside a scope: ```rust +impl<'scope, T> ScopedJoinHandle<'scope, T> { + fn join(self) -> Result; + fn thread(&self) -> &Thread; +} + impl Builder { - fn spawn_scoped<'a, F, T>(self, scope: &Scope<'a>, f: F) -> io::Result> + fn spawn_scoped<'scope, 'env, F, T>( + self, + &'scope Scope<'env>, + f: F, + ) -> io::Result> where - F: FnOnce(&Scope<'a>) -> T + Send + 'a, - T: Send + 'a; + F: FnOnce(&Scope<'env>) -> T + Send + 'env, + T: Send + 'env; } ``` -Note that this interface is a bit simpler than the one in Crossbeam -because we can now merge `JoinHandle` and `ScopedJoinHandle` into a single type. -Moreover, in Crossbeam, `ScopedJoinHandle` is generic over `'scope`, which is -not really necessary for soundness so we can remove that lifetime to simplify -things further. - -It's also worth discussing what exactly happens at the scope end when all +It's also worth pointing out what exactly happens at the scope end when all unjoined threads get automatically joined. If all joins succeed, we take the result of the main closure passed to `scope()` and wrap it inside `Ok`. @@ -229,10 +246,7 @@ several advantages to having them in the standard library: feels like a missing piece in the standard library. * Implementing scoped threads is very tricky to get right so it's good to have a - reliable solution provided by the standard library. Also, scoped threads in `libstd` - will be simpler because we don't need to introduce a special type for - [scoped join handles](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/struct.ScopedJoinHandle.html) - or [builders](https://docs.rs/crossbeam/0.7.1/crossbeam/thread/struct.ScopedThreadBuilder.html). + reliable solution provided by the standard library. * There are many examples in the official documentation and books that could be simplified by scoped threads. @@ -242,7 +256,7 @@ several advantages to having them in the standard library: This is sometimes a problem in unit tests, where "dangling" threads can accumulate if unit tests spawn threads and forget to join them. -* It's indisputable that users keep asking for scoped threads on IRC and forums +* Users keep asking for scoped threads on IRC and forums all the time. Having them as a "blessed" pattern in `std::thread` would be beneficial to everyone. @@ -274,8 +288,7 @@ There are several differences between old and new scoped threads: non-obvious behavior. 4. `ScopedJoinHandle` got parametrized over `'scope` in order to prevent it from - escaping the scope. However, it turns out this is not really necessary for - soundness and was just a conservative safeguard. + escaping the scope. Rayon also has [scopes](https://docs.rs/rayon/1.0.3/rayon/struct.Scope.html), but they work on a different abstraction level - Rayon spawns tasks rather than