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<'env> {
14 env: PhantomData<&'env mut &'env ()>,
17 /// An owned permission to join on a scoped thread (block on its termination).
19 /// See [`Scope::spawn`] for details.
20 pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
22 pub(super) struct ScopeData {
23 n_running_threads: AtomicUsize,
24 a_thread_panicked: AtomicBool,
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");
38 pub(super) fn decrement_n_running_threads(&self, panic: bool) {
40 self.a_thread_panicked.store(true, Ordering::Relaxed);
42 if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 {
43 self.main_thread.unpark();
48 /// Create a scope for spawning scoped threads.
50 /// The function passed to `scope` will be provided a [`Scope`] object,
51 /// through which scoped threads can be [spawned][`Scope::spawn`].
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.
56 /// All threads spawned within the scope that haven't been manually joined
57 /// will be automatically joined before this function returns.
61 /// If any of the automatically joined threads panicked, this function will panic.
63 /// If you want to handle panics from spawned threads,
64 /// [`join`][ScopedJoinHandle::join] them before the end of the scope.
69 /// #![feature(scoped_threads)]
72 /// let mut a = vec![1, 2, 3];
75 /// thread::scope(|s| {
77 /// println!("hello from the first scoped thread");
78 /// // We can borrow `a` here.
82 /// println!("hello from the second scoped thread");
83 /// // We can even mutably borrow `x` here,
84 /// // because no other threads are using it.
87 /// println!("hello from the main thread");
90 /// // After the scope, we can modify and access our variables again:
92 /// assert_eq!(x, a.len());
95 pub fn scope<'env, F, T>(f: F) -> T
97 F: FnOnce(&Scope<'env>) -> T,
101 n_running_threads: AtomicUsize::new(0),
102 main_thread: current(),
103 a_thread_panicked: AtomicBool::new(false),
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)));
111 // Wait until all the threads are finished.
112 while scope.data.n_running_threads.load(Ordering::Acquire) != 0 {
116 // Throw any panic from `f`, or the return value of `f` if no thread panicked.
118 Err(e) => resume_unwind(e),
119 Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => {
120 panic!("a thread panicked")
122 Ok(result) => result,
126 impl<'env> Scope<'env> {
127 /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
129 /// Unlike non-scoped threads, threads spawned with this function may
130 /// borrow non-`'static` data from the outside the scope. See [`scope`] for
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.
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.
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.
147 /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`]
148 /// to recover from such errors.
150 /// [`join`]: ScopedJoinHandle::join
151 pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
153 F: FnOnce(&Scope<'env>) -> T + Send + 'env,
156 Builder::new().spawn_scoped(self, f).expect("failed to spawn thread")
161 /// Spawns a new scoped thread using the settings set through this `Builder`.
163 /// Unlike [`Scope::spawn`], this method yields an [`io::Result`] to
164 /// capture any failure to create the thread at the OS level.
166 /// [`io::Result`]: crate::io::Result
170 /// Panics if a thread name was set and it contained null bytes.
175 /// #![feature(scoped_threads)]
178 /// let mut a = vec![1, 2, 3];
181 /// thread::scope(|s| {
182 /// thread::Builder::new()
183 /// .name("first".to_string())
184 /// .spawn_scoped(s, |_|
186 /// println!("hello from the {:?} scoped thread", thread::current().name());
187 /// // We can borrow `a` here.
191 /// thread::Builder::new()
192 /// .name("second".to_string())
193 /// .spawn_scoped(s, |_|
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];
201 /// println!("hello from the main thread");
204 /// // After the scope, we can modify and access our variables again:
206 /// assert_eq!(x, a.len());
208 pub fn spawn_scoped<'scope, 'env, F, T>(
210 scope: &'scope Scope<'env>,
212 ) -> io::Result<ScopedJoinHandle<'scope, T>>
214 F: FnOnce(&Scope<'env>) -> T + Send + 'env,
217 Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(scope), Some(&scope.data)) }?))
221 impl<'scope, T> ScopedJoinHandle<'scope, T> {
222 /// Extracts a handle to the underlying thread.
227 /// #![feature(scoped_threads)]
228 /// #![feature(thread_is_running)]
232 /// thread::scope(|s| {
233 /// let t = s.spawn(|_| {
234 /// println!("hello");
236 /// println!("thread id: {:?}", t.thread().id());
240 pub fn thread(&self) -> &Thread {
244 /// Waits for the associated thread to finish.
246 /// This function will return immediately if the associated thread has already finished.
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.
254 /// If the associated thread panics, [`Err`] is returned with the panic payload.
256 /// [atomic memory orderings]: crate::sync::atomic
261 /// #![feature(scoped_threads)]
262 /// #![feature(thread_is_running)]
266 /// thread::scope(|s| {
267 /// let t = s.spawn(|_| {
270 /// assert!(t.join().is_err());
273 pub fn join(self) -> Result<T> {
277 /// Checks if the associated thread is still running its main function.
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
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()
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()