mirror of https://github.com/rust-lang/rfcs
Bring ScopedJoinHandle back
This commit is contained in:
parent
d53412a6e9
commit
c38bca67b0
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue