]> git.lizzy.rs Git - rust.git/blobdiff - library/std/src/thread/scoped.rs
Remove argument from closure in thread::Scope::spawn.
[rust.git] / library / std / src / thread / scoped.rs
index cda86a48b24304026d6878ffd26c123b801f289e..e066f97c8e659d36dac66a497409068be35d1ca8 100644 (file)
@@ -9,8 +9,24 @@
 /// A scope to spawn scoped threads in.
 ///
 /// See [`scope`] for details.
-pub struct Scope<'env> {
+pub struct Scope<'scope, 'env: 'scope> {
     data: ScopeData,
+    /// Invariance over 'scope, to make sure 'scope cannot shrink,
+    /// which is necessary for soundness.
+    ///
+    /// Without invariance, this would compile fine but be unsound:
+    ///
+    /// ```compile_fail,E0373
+    /// #![feature(scoped_threads)]
+    ///
+    /// std::thread::scope(|s| {
+    ///     s.spawn(|| {
+    ///         let a = String::from("abcd");
+    ///         s.spawn(|| println!("{:?}", a)); // might run after `a` is dropped
+    ///     });
+    /// });
+    /// ```
+    scope: PhantomData<&'scope mut &'scope ()>,
     env: PhantomData<&'env mut &'env ()>,
 }
 
@@ -20,26 +36,26 @@ pub struct Scope<'env> {
 pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>);
 
 pub(super) struct ScopeData {
-    n_running_threads: AtomicUsize,
+    num_running_threads: AtomicUsize,
     a_thread_panicked: AtomicBool,
     main_thread: Thread,
 }
 
 impl ScopeData {
-    pub(super) fn increment_n_running_threads(&self) {
+    pub(super) fn increment_num_running_threads(&self) {
         // We check for 'overflow' with usize::MAX / 2, to make sure there's no
         // chance it overflows to 0, which would result in unsoundness.
-        if self.n_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
+        if self.num_running_threads.fetch_add(1, Ordering::Relaxed) > usize::MAX / 2 {
             // This can only reasonably happen by mem::forget()'ing many many ScopedJoinHandles.
-            self.decrement_n_running_threads(false);
+            self.decrement_num_running_threads(false);
             panic!("too many running threads in thread scope");
         }
     }
-    pub(super) fn decrement_n_running_threads(&self, panic: bool) {
+    pub(super) fn decrement_num_running_threads(&self, panic: bool) {
         if panic {
             self.a_thread_panicked.store(true, Ordering::Relaxed);
         }
-        if self.n_running_threads.fetch_sub(1, Ordering::Release) == 1 {
+        if self.num_running_threads.fetch_sub(1, Ordering::Release) == 1 {
             self.main_thread.unpark();
         }
     }
@@ -73,12 +89,12 @@ pub(super) fn decrement_n_running_threads(&self, panic: bool) {
 /// let mut x = 0;
 ///
 /// thread::scope(|s| {
-///     s.spawn(|_| {
+///     s.spawn(|| {
 ///         println!("hello from the first scoped thread");
 ///         // We can borrow `a` here.
 ///         dbg!(&a);
 ///     });
-///     s.spawn(|_| {
+///     s.spawn(|| {
 ///         println!("hello from the second scoped thread");
 ///         // We can even mutably borrow `x` here,
 ///         // because no other threads are using it.
@@ -94,22 +110,23 @@ pub(super) fn decrement_n_running_threads(&self, panic: bool) {
 #[track_caller]
 pub fn scope<'env, F, T>(f: F) -> T
 where
-    F: FnOnce(&Scope<'env>) -> T,
+    F: for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> T,
 {
     let scope = Scope {
         data: ScopeData {
-            n_running_threads: AtomicUsize::new(0),
+            num_running_threads: AtomicUsize::new(0),
             main_thread: current(),
             a_thread_panicked: AtomicBool::new(false),
         },
         env: PhantomData,
+        scope: PhantomData,
     };
 
     // Run `f`, but catch panics so we can make sure to wait for all the threads to join.
     let result = catch_unwind(AssertUnwindSafe(|| f(&scope)));
 
     // Wait until all the threads are finished.
-    while scope.data.n_running_threads.load(Ordering::Acquire) != 0 {
+    while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
         park();
     }
 
@@ -117,13 +134,13 @@ pub fn scope<'env, F, T>(f: F) -> T
     match result {
         Err(e) => resume_unwind(e),
         Ok(_) if scope.data.a_thread_panicked.load(Ordering::Relaxed) => {
-            panic!("a thread panicked")
+            panic!("a scoped thread panicked")
         }
         Ok(result) => result,
     }
 }
 
-impl<'env> Scope<'env> {
+impl<'scope, 'env> Scope<'scope, 'env> {
     /// Spawns a new thread within a scope, returning a [`ScopedJoinHandle`] for it.
     ///
     /// Unlike non-scoped threads, threads spawned with this function may
@@ -144,14 +161,14 @@ impl<'env> Scope<'env> {
     ///
     /// # Panics
     ///
-    /// Panics if the OS fails to create a thread; use [`Builder::spawn`]
+    /// Panics if the OS fails to create a thread; use [`Builder::spawn_scoped`]
     /// to recover from such errors.
     ///
     /// [`join`]: ScopedJoinHandle::join
-    pub fn spawn<'scope, F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
+    pub fn spawn<F, T>(&'scope self, f: F) -> ScopedJoinHandle<'scope, T>
     where
-        F: FnOnce(&Scope<'env>) -> T + Send + 'env,
-        T: Send + 'env,
+        F: FnOnce() -> T + Send + 'scope,
+        T: Send + 'scope,
     {
         Builder::new().spawn_scoped(self, f).expect("failed to spawn thread")
     }
@@ -181,7 +198,7 @@ impl Builder {
     /// thread::scope(|s| {
     ///     thread::Builder::new()
     ///         .name("first".to_string())
-    ///         .spawn_scoped(s, |_|
+    ///         .spawn_scoped(s, ||
     ///     {
     ///         println!("hello from the {:?} scoped thread", thread::current().name());
     ///         // We can borrow `a` here.
@@ -190,7 +207,7 @@ impl Builder {
     ///     .unwrap();
     ///     thread::Builder::new()
     ///         .name("second".to_string())
-    ///         .spawn_scoped(s, |_|
+    ///         .spawn_scoped(s, ||
     ///     {
     ///         println!("hello from the {:?} scoped thread", thread::current().name());
     ///         // We can even mutably borrow `x` here,
@@ -207,14 +224,14 @@ impl Builder {
     /// ```
     pub fn spawn_scoped<'scope, 'env, F, T>(
         self,
-        scope: &'scope Scope<'env>,
+        scope: &'scope Scope<'scope, 'env>,
         f: F,
     ) -> io::Result<ScopedJoinHandle<'scope, T>>
     where
-        F: FnOnce(&Scope<'env>) -> T + Send + 'env,
-        T: Send + 'env,
+        F: FnOnce() -> T + Send + 'scope,
+        T: Send + 'scope,
     {
-        Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(scope), Some(&scope.data)) }?))
+        Ok(ScopedJoinHandle(unsafe { self.spawn_unchecked_(|| f(), Some(&scope.data)) }?))
     }
 }
 
@@ -230,7 +247,7 @@ impl<'scope, T> ScopedJoinHandle<'scope, T> {
     /// use std::thread;
     ///
     /// thread::scope(|s| {
-    ///     let t = s.spawn(|_| {
+    ///     let t = s.spawn(|| {
     ///         println!("hello");
     ///     });
     ///     println!("thread id: {:?}", t.thread().id());
@@ -264,7 +281,7 @@ pub fn thread(&self) -> &Thread {
     /// use std::thread;
     ///
     /// thread::scope(|s| {
-    ///     let t = s.spawn(|_| {
+    ///     let t = s.spawn(|| {
     ///         panic!("oh no");
     ///     });
     ///     assert!(t.join().is_err());
@@ -284,11 +301,11 @@ pub fn is_running(&self) -> bool {
     }
 }
 
-impl<'env> fmt::Debug for Scope<'env> {
+impl<'scope, 'env> fmt::Debug for Scope<'scope, 'env> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("Scope")
-            .field("n_running_threads", &self.data.n_running_threads.load(Ordering::Relaxed))
-            .field("a_thread_panicked", &self.data.a_thread_panicked)
+            .field("num_running_threads", &self.data.num_running_threads.load(Ordering::Relaxed))
+            .field("a_thread_panicked", &self.data.a_thread_panicked.load(Ordering::Relaxed))
             .field("main_thread", &self.data.main_thread)
             .finish_non_exhaustive()
     }