1 use super::{current, park, Builder, JoinInner, Result, Thread};
4 use crate::marker::PhantomData;
5 use crate::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
6 use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
9 /// A scope to spawn scoped threads in.
11 /// See [`scope`] for details.
12 pub struct Scope<'scope, 'env: 'scope> {
14 /// Invariance over 'scope, to make sure 'scope cannot shrink,
15 /// which is necessary for soundness.
17 /// Without invariance, this would compile fine but be unsound:
19 /// ```compile_fail,E0373
20 /// #![feature(scoped_threads)]
22 /// std::thread::scope(|s| {
24 /// let a = String::from("abcd");
25 /// s.spawn(|| println!("{a:?}")); // might run after `a` is dropped
29 scope: PhantomData<&'scope mut &'scope ()>,
30 env: PhantomData<&'env mut &'env ()>,
33 /// An owned permission to join on a scoped thread (block on its termination).
35 /// See [`Scope::spawn`] for details.
36 pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
38 pub(super) struct ScopeData {
39 num_running_threads: AtomicUsize,
40 a_thread_panicked: AtomicBool,
45 pub(super) fn increment_num_running_threads(&self) {
46 // We check for 'overflow' with usize::MAX / 2, to make sure there's no
47 // chance it overflows to 0, which would result in unsoundness.
48 if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
49 // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles.
50 self.decrement_num_running_threads(false);
51 panic!("too many running threads in thread scope");
54 pub(super) fn decrement_num_running_threads(&self, panic: bool) {
56 self.a_thread_panicked.store(true, Ordering::Relaxed);
58 if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 {
59 self.main_thread.unpark();
64 /// Create a scope for spawning scoped threads.
66 /// The function passed to `scope` will be provided a [`Scope`] object,
67 /// through which scoped threads can be [spawned][`Scope::spawn`].
69 /// Unlike non-scoped threads, scoped threads can borrow non-`'static` data,
70 /// as the scope guarantees all threads will be joined at the end of the scope.
72 /// All threads spawned within the scope that haven't been manually joined
73 /// will be automatically joined before this function returns.
77 /// If any of the automatically joined threads panicked, this function will panic.
79 /// If you want to handle panics from spawned threads,
80 /// [`join`][ScopedJoinHandle::join] them before the end of the scope.
85 /// #![feature(scoped_threads)]
88 /// let mut a = vec![1, 2, 3];
91 /// thread::scope(|s| {
93 /// println!("hello from the first scoped thread");
94 /// // We can borrow `a` here.
98 /// println!("hello from the second scoped thread");
99 /// // We can even mutably borrow `x` here,
100 /// // because no other threads are using it.
101 /// x += a[0] + a[2];
103 /// println!("hello from the main thread");
106 /// // After the scope, we can modify and access our variables again:
108 /// assert_eq!(x, a.len());
113 /// Scoped threads involve two lifetimes: `'scope` and `'env`.
115 /// The `'scope` lifetime represents the lifetime of the scope itself.
116 /// That is: the time during which new scoped threads may be spawned,
117 /// and also the time during which they might still be running.
118 /// Once this lifetime ends, all scoped threads are joined.
119 /// This lifetime starts within the `scope` function, before `f` (the argument to `scope`) starts.
120 /// It ends after `f` returns and all scoped threads have been joined, but before `scope` returns.
122 /// The `'env` lifetime represents the lifetime of whatever is borrowed by the scoped threads.
123 /// This lifetime must outlast the call to `scope`, and thus cannot be smaller than `'scope`.
124 /// It can be as small as the call to `scope`, meaning that anything that outlives this call,
125 /// such as local variables defined right before the scope, can be borrowed by the scoped threads.
127 /// The `'env: 'scope` bound is part of the definition of the `Scope` type.
129 pub fn scope<'env, F, T>(f: F) -> T
131 F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
135 num_running_threads: AtomicUsize::new(0),
136 main_thread: current(),
137 a_thread_panicked: AtomicBool::new(false),
143 // Run `f`, but catch panics so we can make sure to wait for all the threads to join.
144 let result = catch_unwind(AssertUnwindSafe(|| f(&scope)));
146 // Wait until all the threads are finished.
147 while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
151 // Throw any panic from `f`, or the return value of `f` if no thread panicked.
153 Err(e) => resume_unwind(e),
154 Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => {
155 panic!("a scoped thread panicked")
157 Ok(result) => result,
161 impl<'scope, 'env> Scope<'scope, 'env> {
162 /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
164 /// Unlike non-scoped threads, threads spawned with this function may
165 /// borrow non-`'static` data from the outside the scope. See [`scope`] for
168 /// The join handle provides a [`join`] method that can be used to join the spawned
169 /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
170 /// the panic payload.
172 /// If the join handle is dropped, the spawned thread will implicitly joined at the
173 /// end of the scope. In that case, if the spawned thread panics, [`scope`] will
174 /// panic after all threads are joined.
176 /// This call will create a thread using default parameters of [`Builder`].
177 /// If you want to specify the stack size or the name of the thread, use
178 /// [`Builder::spawn_scoped`] instead.
182 /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`]
183 /// to recover from such errors.
185 /// [`join`]: ScopedJoinHandle::join
186 pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
188 F: FnOnce() -> T + Send + 'scope,
191 Builder::new().spawn_scoped(self, f).expect("failed to spawn thread")
196 /// Spawns a new scoped thread using the settings set through this `Builder`.
198 /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to
199 /// capture any failure to create the thread at the OS level.
201 /// [`io::Result`]: crate::io::Result
205 /// Panics if a thread name was set and it contained null bytes.
210 /// #![feature(scoped_threads)]
213 /// let mut a = vec![1, 2, 3];
216 /// thread::scope(|s| {
217 /// thread::Builder::new()
218 /// .name("first".to_string())
219 /// .spawn_scoped(s, ||
221 /// println!("hello from the {:?} scoped thread", thread::current().name());
222 /// // We can borrow `a` here.
226 /// thread::Builder::new()
227 /// .name("second".to_string())
228 /// .spawn_scoped(s, ||
230 /// println!("hello from the {:?} scoped thread", thread::current().name());
231 /// // We can even mutably borrow `x` here,
232 /// // because no other threads are using it.
233 /// x += a[0] + a[2];
236 /// println!("hello from the main thread");
239 /// // After the scope, we can modify and access our variables again:
241 /// assert_eq!(x, a.len());
243 pub fn spawn_scoped<'scope, 'env, F, T>(
245 scope: &'scope Scope<'scope, 'env>,
247 ) -> io::Result<ScopedJoinHandle<'scope, T>>
249 F: FnOnce() -> T + Send + 'scope,
252 Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(f, Some(&scope.data)) }?))
256 impl<'scope, T> ScopedJoinHandle<'scope, T> {
257 /// Extracts a handle to the underlying thread.
262 /// #![feature(scoped_threads)]
266 /// thread::scope(|s| {
267 /// let t = s.spawn(|| {
268 /// println!("hello");
270 /// println!("thread id: {:?}", t.thread().id());
274 pub fn thread(&self) -> &Thread {
278 /// Waits for the associated thread to finish.
280 /// This function will return immediately if the associated thread has already finished.
282 /// In terms of [atomic memory orderings], the completion of the associated
283 /// thread synchronizes with this function returning.
284 /// In other words, all operations performed by that thread
285 /// [happen before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses)
286 /// all operations that happen after `join` returns.
288 /// If the associated thread panics, [`Err`] is returned with the panic payload.
290 /// [atomic memory orderings]: crate::sync::atomic
295 /// #![feature(scoped_threads)]
299 /// thread::scope(|s| {
300 /// let t = s.spawn(|| {
303 /// assert!(t.join().is_err());
306 pub fn join(self) -> Result<T> {
310 /// Checks if the associated thread has finished running its main function.
312 /// This might return `true` for a brief moment after the thread's main
313 /// function has returned, but before the thread itself has stopped running.
314 /// However, once this returns `true`, [`join`][Self::join] can be expected
315 /// to return quickly, without blocking for any significant amount of time.
317 /// This function does not block. To block while waiting on the thread to finish,
318 /// use [`join`][Self::join].
319 pub fn is_finished(&self) -> bool {
320 Arc::strong_count(&self.0.packet) == 1
324 impl fmt::Debug for Scope<'_, '_> {
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 f.debug_struct("Scope")
327 .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed))
328 .field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed))
329 .field("main_thread", &self.data.main_thread)
330 .finish_non_exhaustive()
334 impl<'scope, T> fmt::Debug for ScopedJoinHandle<'scope, T> {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 f.debug_struct("ScopedJoinHandle").finish_non_exhaustive()