Bring ScopedJoinHandle back

This commit is contained in:
Stjepan Glavina 2019-03-03 20:47:15 +01:00
parent d53412a6e9
commit c38bca67b0
1 changed files with 41 additions and 28 deletions

View File

@ -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<T>
fn scope<'env, F, T>(f: F) -> Result<T>
where
F: FnOnce(&Scope<'a>) -> T;
F: FnOnce(&Scope<'env>) -> T;
impl<'a> Scope<'a> {
fn spawn<F, T>(&self, f: F) -> JoinHandle<T>
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<T>;
fn thread(&self) -> &Thread;
}
impl Builder {
fn spawn_scoped<'a, F, T>(self, scope: &Scope<'a>, f: F) -> io::Result<JoinHandle<T>>
fn spawn_scoped<'scope, 'env, F, T>(
self,
&'scope Scope<'env>,
f: F,
) -> io::Result<ScopedJoinHandle<'scope, T>>
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