]> git.lizzy.rs Git - rust.git/blob - library/std/src/thread/scoped.rs
Fix typo in Scope::spawn docs.
[rust.git] / library / std / src / thread / scoped.rs
1 use super::{current, park, Builder, JoinInner, Result, Thread};
2 use crate::fmt;
3 use crate::io;
4 use crate::marker::PhantomData;
5 use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
6 use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
7 use crate::sync::Arc;
8
9 /// A scope to spawn scoped threads in.
10 ///
11 /// See [`scope`] for details.
12 pub struct Scope<'env> {
13     data: ScopeData,
14     env: PhantomData<&'env mut &'env ()>,
15 }
16
17 /// An owned permission to join on a scoped thread (block on its termination).
18 ///
19 /// See [`Scope::spawn`] for details.
20 pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
21
22 pub(super) struct ScopeData {
23     n_running_threads: AtomicUsize,
24     a_thread_panicked: AtomicBool,
25     main_thread: Thread,
26 }
27
28 impl ScopeData {
29     pub(super) fn increment_n_running_threads(&self) {
30         // We check for 'overflow' with usize::MAX / 2, to make sure there's no
31         // chance it overflows to 0, which would result in unsoundness.
32         if self.n_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
33             // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles.
34             self.decrement_n_running_threads(false);
35             panic!("too many running threads in thread scope");
36         }
37     }
38     pub(super) fn decrement_n_running_threads(&self, panic: bool) {
39         if panic {
40             self.a_thread_panicked.store(true, Ordering::Relaxed);
41         }
42         if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 {
43             self.main_thread.unpark();
44         }
45     }
46 }
47
48 /// Create a scope for spawning scoped threads.
49 ///
50 /// The function passed to `scope` will be provided a [`Scope`] object,
51 /// through which scoped threads can be [spawned][`Scope::spawn`].
52 ///
53 /// Unlike non-scoped threads, scoped threads can borrow non-`'static` data,
54 /// as the scope guarantees all threads will be joined at the end of the scope.
55 ///
56 /// All threads spawned within the scope that haven't been manually joined
57 /// will be automatically joined before this function returns.
58 ///
59 /// # Panics
60 ///
61 /// If any of the automatically joined threads panicked, this function will panic.
62 ///
63 /// If you want to handle panics from spawned threads,
64 /// [`join`][ScopedJoinHandle::join] them before the end of the scope.
65 ///
66 /// # Example
67 ///
68 /// ```
69 /// #![feature(scoped_threads)]
70 /// use std::thread;
71 ///
72 /// let mut a = vec![1, 2, 3];
73 /// let mut x = 0;
74 ///
75 /// thread::scope(|s| {
76 ///     s.spawn(|_| {
77 ///         println!("hello from the first scoped thread");
78 ///         // We can borrow `a` here.
79 ///         dbg!(&a);
80 ///     });
81 ///     s.spawn(|_| {
82 ///         println!("hello from the second scoped thread");
83 ///         // We can even mutably borrow `x` here,
84 ///         // because no other threads are using it.
85 ///         x += a[0] + a[2];
86 ///     });
87 ///     println!("hello from the main thread");
88 /// });
89 ///
90 /// // After the scope, we can modify and access our variables again:
91 /// a.push(4);
92 /// assert_eq!(x, a.len());
93 /// ```
94 #[track_caller]
95 pub fn scope<'env, F, T>(f: F) -> T
96 where
97     F: FnOnce(&Scope<'env>) -> T,
98 {
99     let scope = Scope {
100         data: ScopeData {
101             n_running_threads: AtomicUsize::new(0),
102             main_thread: current(),
103             a_thread_panicked: AtomicBool::new(false),
104         },
105         env: PhantomData,
106     };
107
108     // Run `f`, but catch panics so we can make sure to wait for all the threads to join.
109     let result = catch_unwind(AssertUnwindSafe(|| f(&scope)));
110
111     // Wait until all the threads are finished.
112     while scope.data.n_running_threads.load(Ordering::Acquire) != 0 {
113         park();
114     }
115
116     // Throw any panic from `f`, or the return value of `f` if no thread panicked.
117     match result {
118         Err(e) => resume_unwind(e),
119         Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => {
120             panic!("a thread panicked")
121         }
122         Ok(result) => result,
123     }
124 }
125
126 impl<'env> Scope<'env> {
127     /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
128     ///
129     /// Unlike non-scoped threads, threads spawned with this function may
130     /// borrow non-`'static` data from the outside the scope. See [`scope`] for
131     /// details.
132     ///
133     /// The join handle provides a [`join`] method that can be used to join the spawned
134     /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
135     /// the panic payload.
136     ///
137     /// If the join handle is dropped, the spawned thread will implicitly joined at the
138     /// end of the scope. In that case, if the spawned thread panics, [`scope`] will
139     /// panic after all threads are joined.
140     ///
141     /// This call will create a thread using default parameters of [`Builder`].
142     /// If you want to specify the stack size or the name of the thread, use
143     /// [`Builder::spawn_scoped`] instead.
144     ///
145     /// # Panics
146     ///
147     /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`]
148     /// to recover from such errors.
149     ///
150     /// [`join`]: ScopedJoinHandle::join
151     pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
152     where
153         F: FnOnce(&Scope<'env>) -> T + Send + 'env,
154         T: Send + 'env,
155     {
156         Builder::new().spawn_scoped(self, f).expect("failed to spawn thread")
157     }
158 }
159
160 impl Builder {
161     /// Spawns a new scoped thread using the settings set through this `Builder`.
162     ///
163     /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to
164     /// capture any failure to create the thread at the OS level.
165     ///
166     /// [`io::Result`]: crate::io::Result
167     ///
168     /// # Panics
169     ///
170     /// Panics if a thread name was set and it contained null bytes.
171     ///
172     /// # Example
173     ///
174     /// ```
175     /// #![feature(scoped_threads)]
176     /// use std::thread;
177     ///
178     /// let mut a = vec![1, 2, 3];
179     /// let mut x = 0;
180     ///
181     /// thread::scope(|s| {
182     ///     thread::Builder::new()
183     ///         .name("first".to_string())
184     ///         .spawn_scoped(s, |_|
185     ///     {
186     ///         println!("hello from the {:?} scoped thread", thread::current().name());
187     ///         // We can borrow `a` here.
188     ///         dbg!(&a);
189     ///     })
190     ///     .unwrap();
191     ///     thread::Builder::new()
192     ///         .name("second".to_string())
193     ///         .spawn_scoped(s, |_|
194     ///     {
195     ///         println!("hello from the {:?} scoped thread", thread::current().name());
196     ///         // We can even mutably borrow `x` here,
197     ///         // because no other threads are using it.
198     ///         x += a[0] + a[2];
199     ///     })
200     ///     .unwrap();
201     ///     println!("hello from the main thread");
202     /// });
203     ///
204     /// // After the scope, we can modify and access our variables again:
205     /// a.push(4);
206     /// assert_eq!(x, a.len());
207     /// ```
208     pub fn spawn_scoped<'scope, 'env, F, T>(
209         self,
210         scope: &'scope Scope<'env>,
211         f: F,
212     ) -> io::Result<ScopedJoinHandle<'scope, T>>
213     where
214         F: FnOnce(&Scope<'env>) -> T + Send + 'env,
215         T: Send + 'env,
216     {
217         Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(scope), Some(&scope.data)) }?))
218     }
219 }
220
221 impl<'scope, T> ScopedJoinHandle<'scope, T> {
222     /// Extracts a handle to the underlying thread.
223     ///
224     /// # Examples
225     ///
226     /// ```
227     /// #![feature(scoped_threads)]
228     /// #![feature(thread_is_running)]
229     ///
230     /// use std::thread;
231     ///
232     /// thread::scope(|s| {
233     ///     let t = s.spawn(|_| {
234     ///         println!("hello");
235     ///     });
236     ///     println!("thread id: {:?}", t.thread().id());
237     /// });
238     /// ```
239     #[must_use]
240     pub fn thread(&self) -> &Thread {
241         &self.0.thread
242     }
243
244     /// Waits for the associated thread to finish.
245     ///
246     /// This function will return immediately if the associated thread has already finished.
247     ///
248     /// In terms of [atomic memory orderings], the completion of the associated
249     /// thread synchronizes with this function returning.
250     /// In other words, all operations performed by that thread
251     /// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses)
252     /// all operations that happen after `join` returns.
253     ///
254     /// If the associated thread panics, [`Err`] is returned with the panic payload.
255     ///
256     /// [atomic memory orderings]: crate::sync::atomic
257     ///
258     /// # Examples
259     ///
260     /// ```
261     /// #![feature(scoped_threads)]
262     /// #![feature(thread_is_running)]
263     ///
264     /// use std::thread;
265     ///
266     /// thread::scope(|s| {
267     ///     let t = s.spawn(|_| {
268     ///         panic!("oh no");
269     ///     });
270     ///     assert!(t.join().is_err());
271     /// });
272     /// ```
273     pub fn join(self) -> Result<T> {
274         self.0.join()
275     }
276
277     /// Checks if the associated thread is still running its main function.
278     ///
279     /// This might return `false` for a brief moment after the thread's main
280     /// function has returned, but before the thread itself has stopped running.
281     #[unstable(feature = "thread_is_running", issue = "90470")]
282     pub fn is_running(&self) -> bool {
283         Arc::strong_count(&self.0.packet) > 1
284     }
285 }
286
287 impl<'env> fmt::Debug for Scope<'env> {
288     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289         f.debug_struct("Scope")
290             .field("n_running_threads", &self.data.n_running_threads.load(Ordering::Relaxed))
291             .field("a_thread_panicked", &self.data.a_thread_panicked)
292             .field("main_thread", &self.data.main_thread)
293             .finish_non_exhaustive()
294     }
295 }
296
297 impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> {
298     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
299         f.debug_struct("ScopedJoinHandle").finish_non_exhaustive()
300     }
301 }