Skip to content

Commit da33da1

Browse files
committed
Add documentation for scoped threads.
1 parent cc699e1 commit da33da1

File tree

1 file changed

+175
-11
lines changed

1 file changed

+175
-11
lines changed

library/std/src/thread/scoped.rs

+175-11
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
77
use crate::sync::atomic::{AtomicUsize, Ordering};
88
use crate::sync::{Arc, Mutex};
99

10-
/// TODO: documentation
10+
/// A scope to spawn scoped threads in.
11+
///
12+
/// See [`scope`] for details.
1113
pub struct Scope<'env> {
1214
data: ScopeData,
1315
env: PhantomData<&'env ()>,
1416
}
1517

16-
/// TODO: documentation
18+
/// An owned permission to join on a scoped thread (block on its termination).
19+
///
20+
/// See [`Scope::spawn`] for details.
1721
pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
1822

1923
pub(super) struct ScopeData {
@@ -39,7 +43,52 @@ impl ScopeData {
3943
}
4044
}
4145

42-
/// TODO: documentation
46+
/// Create a scope for spawning scoped threads.
47+
///
48+
/// The function passed to `scope` will be provided a [`Scope`] object,
49+
/// through which scoped threads can be [spawned][`Scope::spawn`].
50+
///
51+
/// Unlike non-scoped threads, scoped threads can non-`'static` data,
52+
/// as the scope guarantees all threads will be joined at the end of the scope.
53+
///
54+
/// All threads spawned within the scope that haven't been manually joined
55+
/// will be automatically joined before this function returns.
56+
///
57+
/// # Panics
58+
///
59+
/// If any of the automatically joined threads panicked, this function will panic.
60+
///
61+
/// If you want to handle panics from spawned threads,
62+
/// [`join`][ScopedJoinHandle::join] them before the end of the scope.
63+
///
64+
/// # Example
65+
///
66+
/// ```
67+
/// #![feature(scoped_threads)]
68+
/// use std::thread;
69+
///
70+
/// let mut a = vec![1, 2, 3];
71+
/// let mut x = 0;
72+
///
73+
/// thread::scope(|s| {
74+
/// s.spawn(|_| {
75+
/// println!("hello from the first scoped thread");
76+
/// // We can borrow `a` here.
77+
/// dbg!(&a);
78+
/// });
79+
/// s.spawn(|_| {
80+
/// println!("hello from the second scoped thread");
81+
/// // We can even mutably borrow `x` here,
82+
/// // because no other threads are using it.
83+
/// x += a[0] + a[2];
84+
/// });
85+
/// println!("hello from the main thread");
86+
/// });
87+
///
88+
/// // After the scope, we can modify and access our variables again:
89+
/// a.push(4);
90+
/// assert_eq!(x, a.len());
91+
/// ```
4392
pub fn scope<'env, F, T>(f: F) -> T
4493
where
4594
F: FnOnce(&Scope<'env>) -> T,
@@ -80,7 +129,30 @@ where
80129
}
81130

82131
impl<'env> Scope<'env> {
83-
/// TODO: documentation
132+
/// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
133+
///
134+
/// Unlike non-scoped threads, threads spawned with this function may
135+
/// borrow non-`'static` data from the outside the scope. See [`scope`] for
136+
/// details.
137+
///
138+
/// The join handle provides a [`join`] method that can be used to join the spawned
139+
/// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
140+
/// the panic payload.
141+
///
142+
/// If the join handle is dropped, the spawned thread will implicitly joined at the
143+
/// end of the scope. In that case, if the spawned thread panics, [`scope`] will
144+
/// panic after all threads are joined.
145+
///
146+
/// This call will create a thread using default parameters of [`Builder`].
147+
/// If you want to specify the stack size or the name of the thread, use
148+
/// [`Builder::spawn_scoped`] instead.
149+
///
150+
/// # Panics
151+
///
152+
/// Panics if the OS fails to create a thread; use [`Builder::spawn`]
153+
/// to recover from such errors.
154+
///
155+
/// [`join`]: ScopedJoinHandle::join
84156
pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
85157
where
86158
F: FnOnce(&Scope<'env>) -> T + Send + 'env,
@@ -91,7 +163,54 @@ impl<'env> Scope<'env> {
91163
}
92164

93165
impl Builder {
94-
fn spawn_scoped<'scope, 'env, F, T>(
166+
/// Spawns a new scoped thread using the settings set through this `Builder`.
167+
///
168+
/// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to
169+
/// capture any failure to create the thread at the OS level.
170+
///
171+
/// [`io::Result`]: crate::io::Result
172+
///
173+
/// # Panics
174+
///
175+
/// Panics if a thread name was set and it contained null bytes.
176+
///
177+
/// # Example
178+
///
179+
/// ```
180+
/// #![feature(scoped_threads)]
181+
/// use std::thread;
182+
///
183+
/// let mut a = vec![1, 2, 3];
184+
/// let mut x = 0;
185+
///
186+
/// thread::scope(|s| {
187+
/// thread::Builder::new()
188+
/// .name("first".to_string())
189+
/// .spawn_scoped(s, |_|
190+
/// {
191+
/// println!("hello from the {:?} scoped thread", thread::current().name());
192+
/// // We can borrow `a` here.
193+
/// dbg!(&a);
194+
/// })
195+
/// .unwrap();
196+
/// thread::Builder::new()
197+
/// .name("second".to_string())
198+
/// .spawn_scoped(s, |_|
199+
/// {
200+
/// println!("hello from the {:?} scoped thread", thread::current().name());
201+
/// // We can even mutably borrow `x` here,
202+
/// // because no other threads are using it.
203+
/// x += a[0] + a[2];
204+
/// })
205+
/// .unwrap();
206+
/// println!("hello from the main thread");
207+
/// });
208+
///
209+
/// // After the scope, we can modify and access our variables again:
210+
/// a.push(4);
211+
/// assert_eq!(x, a.len());
212+
/// ```
213+
pub fn spawn_scoped<'scope, 'env, F, T>(
95214
self,
96215
scope: &'scope Scope<'env>,
97216
f: F,
@@ -105,16 +224,61 @@ impl Builder {
105224
}
106225

107226
impl<'scope, T> ScopedJoinHandle<'scope, T> {
108-
/// TODO
109-
pub fn join(self) -> Result<T> {
110-
self.0.join()
111-
}
112-
113-
/// TODO
227+
/// Extracts a handle to the underlying thread.
228+
///
229+
/// # Examples
230+
///
231+
/// ```
232+
/// #![feature(scoped_threads)]
233+
/// #![feature(thread_is_running)]
234+
///
235+
/// use std::thread;
236+
///
237+
/// thread::scope(|s| {
238+
/// let t = s.spawn(|_| {
239+
/// println!("hello");
240+
/// });
241+
/// println!("thread id: {:?}", t.thread().id());
242+
/// });
243+
/// ```
244+
#[must_use]
114245
pub fn thread(&self) -> &Thread {
115246
&self.0.thread
116247
}
117248

249+
/// Waits for the associated thread to finish.
250+
///
251+
/// This function will return immediately if the associated thread has already finished.
252+
///
253+
/// In terms of [atomic memory orderings], the completion of the associated
254+
/// thread synchronizes with this function returning.
255+
/// In other words, all operations performed by that thread
256+
/// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses)
257+
/// all operations that happen after `join` returns.
258+
///
259+
/// If the associated thread panics, [`Err`] is returned with the panic payload.
260+
///
261+
/// [atomic memory orderings]: crate::sync::atomic
262+
///
263+
/// # Examples
264+
///
265+
/// ```
266+
/// #![feature(scoped_threads)]
267+
/// #![feature(thread_is_running)]
268+
///
269+
/// use std::thread;
270+
///
271+
/// thread::scope(|s| {
272+
/// let t = s.spawn(|_| {
273+
/// panic!("oh no");
274+
/// });
275+
/// assert!(t.join().is_err());
276+
/// });
277+
/// ```
278+
pub fn join(self) -> Result<T> {
279+
self.0.join()
280+
}
281+
118282
/// Checks if the the associated thread is still running its main function.
119283
///
120284
/// This might return `false` for a brief moment after the thread's main

0 commit comments

Comments
 (0)