Skip to main content

leak_playground_std/
thread.rs

1//! Possible [`std::thread`] additions. Contains examples.
2//!
3//! # Examples
4//!
5//! Static JoinGuard self ownership
6//!
7//! ```
8//! use leak_playground_std::*;
9//! let (tx, rx) = sync::mpsc::rendezvous_channel();
10//! let thrd = thread::spawn_scoped(move || {
11//!     let _this_thread = rx.recv().unwrap();
12//! });
13//! tx.send(thrd).unwrap();
14//! ```
15
16use std::thread::JoinHandle;
17use std::{marker::PhantomData, thread};
18
19use crate::marker::{Forget, Unforget};
20use crate::mem::{self, ManuallyDrop};
21use crate::rc::Rc;
22use crate::sync::Arc;
23
24/// Spawn borrowing thread handles.
25pub fn spawn_scoped<'a, F, T>(f: F) -> JoinGuard<'a, T>
26where
27    F: FnOnce() -> T + Send + 'a,
28    T: Send + 'a,
29{
30    JoinGuard {
31        // SAFETY: destruction guarantee from `Unforget<&'a ()>` and `T: 'a`
32        child: unsafe {
33            ManuallyDrop::new_unchecked(thread::Builder::new().spawn_unchecked(f).unwrap())
34        },
35        _borrow: Unforget::new(PhantomData),
36        _unsend: PhantomData,
37    }
38}
39
40/// Handle to a thread, which joins on drop.
41///
42/// Cannot be sent across threads.
43/// This is made to ensure we won't put this into itself, thus forgetting it.
44///
45/// To spawn use [`spawn_scoped`].
46pub struct JoinGuard<'a, T> {
47    child: ManuallyDrop<thread::JoinHandle<T>>,
48
49    /// Not sure about covariance there.
50    _borrow: Unforget<'static, PhantomData<&'a ()>>,
51    _unsend: PhantomData<*mut ()>,
52}
53
54unsafe impl<T> Send for JoinGuard<'_, T> where Self: Forget {}
55unsafe impl<T> Sync for JoinGuard<'_, T> {}
56
57impl<T> JoinGuard<'_, T> {
58    pub fn join(mut self) -> std::thread::Result<T> {
59        let join_handle;
60        // SAFETY: we immediately, join after
61        unsafe {
62            join_handle = ManuallyDrop::take(&mut self.child);
63            // need this to avoid calling `JoinGuard::drop`
64            mem::forget_unchecked(self);
65        }
66        join_handle.join()
67    }
68
69    pub fn thread(&self) -> &std::thread::Thread {
70        self.child.thread()
71    }
72
73    pub fn is_finished(&self) -> bool {
74        self.child.is_finished()
75    }
76
77    pub fn into_rc(self) -> Rc<Self> {
78        // SAFETY: we cannot move Rc<JoinGuard> into it's closure because
79        //   impl !Send for Rc<JoinGuard>
80        unsafe { Rc::new_unchecked(self) }
81    }
82
83    pub fn into_arc(self) -> Arc<Self> {
84        // SAFETY: we cannot move Arc<JoinGuard> into it's closure because
85        //   Arc<JoinGuard>: !Send, or otherwise JoinGuard: Forget
86        unsafe { Arc::new_unchecked(self) }
87    }
88}
89
90impl<T> JoinGuard<'static, T> {
91    pub fn into_handle(self) -> JoinHandle<T> {
92        self.into()
93    }
94
95    pub fn detach(self) {
96        let _ = self.into_handle();
97    }
98}
99
100impl<T> From<JoinGuard<'static, T>> for JoinHandle<T> {
101    fn from(mut value: JoinGuard<'static, T>) -> Self {
102        unsafe { ManuallyDrop::take(&mut value.child) }
103    }
104}
105
106impl<'a, T> Drop for JoinGuard<'a, T> {
107    fn drop(&mut self) {
108        let join_handle = unsafe { ManuallyDrop::take(&mut self.child) };
109        // Shouldn't panic
110        let child = join_handle.thread().clone();
111        // No panic since we guarantee that we would never join on ourselves,
112        // except when `Self: Forget`, then we don't care.
113        let res = join_handle.join();
114        // Propagating panic there since structured parallelism, but ignoring
115        // during panic. Anyway child thread is joined thus either would
116        // be fine.
117        if res.is_err() && !std::thread::panicking() {
118            panic!("child thread {child:?} panicked");
119        }
120    }
121}