Skip to main content

std/thread/
lifecycle.rs

1//! The inner logic for thread spawning and joining.
2
3use super::current::set_current;
4use super::id::ThreadId;
5use super::scoped::ScopeData;
6use super::thread::Thread;
7use super::{Result, spawnhook};
8use crate::cell::UnsafeCell;
9use crate::marker::PhantomData;
10use crate::mem::MaybeDangling;
11use crate::sync::Arc;
12use crate::sync::atomic::{Atomic, AtomicUsize, Ordering};
13use crate::sys::{AsInner, IntoInner, thread as imp};
14use crate::{env, io, panic};
15
16#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
17pub(super) unsafe fn spawn_unchecked<'scope, F, T>(
18    name: Option<String>,
19    stack_size: Option<usize>,
20    no_hooks: bool,
21    scope_data: Option<Arc<ScopeData>>,
22    f: F,
23) -> io::Result<JoinInner<'scope, T>>
24where
25    F: FnOnce() -> T,
26    F: Send,
27    T: Send,
28{
29    let stack_size = stack_size.unwrap_or_else(|| {
30        static MIN: Atomic<usize> = AtomicUsize::new(0);
31
32        match MIN.load(Ordering::Relaxed) {
33            0 => {}
34            n => return n - 1,
35        }
36
37        let amt = env::var_os("RUST_MIN_STACK")
38            .and_then(|s| s.to_str().and_then(|s| s.parse().ok()))
39            .unwrap_or(imp::DEFAULT_MIN_STACK_SIZE);
40
41        // 0 is our sentinel value, so ensure that we'll never see 0 after
42        // initialization has run
43        MIN.store(amt + 1, Ordering::Relaxed);
44        amt
45    });
46
47    let id = ThreadId::new();
48    let thread = Thread::new(id, name);
49
50    let hooks = if no_hooks {
51        spawnhook::ChildSpawnHooks::default()
52    } else {
53        spawnhook::run_spawn_hooks(&thread)
54    };
55
56    let my_packet: Arc<Packet<'scope, T>> =
57        Arc::new(Packet { scope: scope_data, result: UnsafeCell::new(None), _marker: PhantomData });
58    let their_packet = my_packet.clone();
59
60    // Pass `f` in `MaybeDangling` because actually that closure might *run longer than the lifetime of `F`*.
61    // See <https://github.com/rust-lang/rust/issues/101983> for more details.
62    let f = MaybeDangling::new(f);
63
64    // The entrypoint of the Rust thread, after platform-specific thread
65    // initialization is done.
66    let rust_start = move || {
67        let f = f.into_inner();
68        let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
69            crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.inherit_and_run());
70            crate::sys::backtrace::__rust_begin_short_backtrace(f)
71        }));
72        // SAFETY: `their_packet` as been built just above and moved by the
73        // closure (it is an Arc<...>) and `my_packet` will be stored in the
74        // same `JoinInner` as this closure meaning the mutation will be
75        // safe (not modify it and affect a value far away).
76        unsafe { *their_packet.result.get() = Some(try_result) };
77        // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that
78        // will call `decrement_num_running_threads` and therefore signal that this thread is
79        // done.
80        drop(their_packet);
81        // Here, the lifetime `'scope` can end. `main` keeps running for a bit
82        // after that before returning itself.
83    };
84
85    if let Some(scope_data) = &my_packet.scope {
86        scope_data.increment_num_running_threads();
87    }
88
89    // SAFETY: dynamic size and alignment of the Box remain the same. See below for why the
90    // lifetime change is justified.
91    let rust_start = unsafe {
92        let ptr = Box::into_raw(Box::new(rust_start));
93        let ptr = crate::mem::transmute::<
94            *mut (dyn FnOnce() + Send + '_),
95            *mut (dyn FnOnce() + Send + 'static),
96        >(ptr);
97        Box::from_raw(ptr)
98    };
99
100    let init = Box::new(ThreadInit { handle: thread.clone(), rust_start });
101
102    Ok(JoinInner {
103        // SAFETY:
104        //
105        // `imp::Thread::new` takes a closure with a `'static` lifetime, since it's passed
106        // through FFI or otherwise used with low-level threading primitives that have no
107        // notion of or way to enforce lifetimes.
108        //
109        // As mentioned in the `Safety` section of this function's documentation, the caller of
110        // this function needs to guarantee that the passed-in lifetime is sufficiently long
111        // for the lifetime of the thread.
112        //
113        // Similarly, the `sys` implementation must guarantee that no references to the closure
114        // exist after the thread has terminated, which is signaled by `Thread::join`
115        // returning.
116        native: unsafe { imp::Thread::new(stack_size, init)? },
117        thread,
118        packet: my_packet,
119    })
120}
121
122/// The data passed to the spawned thread for thread initialization. Any thread
123/// implementation should start a new thread by calling .init() on this before
124/// doing anything else to ensure the current thread is properly initialized and
125/// the global allocator works.
126pub(crate) struct ThreadInit {
127    pub handle: Thread,
128    pub rust_start: Box<dyn FnOnce() + Send>,
129}
130
131impl ThreadInit {
132    /// Initialize the 'current thread' mechanism on this thread, returning the
133    /// Rust entry point.
134    pub(crate) fn init(self: Box<Self>) -> Box<dyn FnOnce() + Send> {
135        // Set the current thread before any (de)allocations on the global allocator occur,
136        // so that it may call std::thread::current() in its implementation. This is also
137        // why we take Box<Self>, to ensure the Box is not destroyed until after this point.
138        // Cloning the handle does not invoke the global allocator, it is an Arc.
139        if let Err(_thread) = set_current(self.handle.clone()) {
140            // The current thread should not have set yet. Use an abort to save binary size (see #123356).
141            rtabort!("current thread handle already set during thread spawn");
142        }
143
144        // The handle was created by the spawning thread, so only now that we are
145        // running can the OS id be filled in.
146        self.handle.set_os_id_to_current();
147
148        if let Some(name) = self.handle.cname() {
149            imp::set_name(name);
150        }
151
152        self.rust_start
153    }
154}
155
156// This packet is used to communicate the return value between the spawned
157// thread and the rest of the program. It is shared through an `Arc` and
158// there's no need for a mutex here because synchronization happens with `join()`
159// (the caller will never read this packet until the thread has exited).
160//
161// An Arc to the packet is stored into a `JoinInner` which in turns is placed
162// in `JoinHandle`.
163struct Packet<'scope, T> {
164    scope: Option<Arc<ScopeData>>,
165    result: UnsafeCell<Option<Result<T>>>,
166    _marker: PhantomData<Option<&'scope ScopeData>>,
167}
168
169// Due to the usage of `UnsafeCell` we need to manually implement Sync.
170// The type `T` should already always be Send (otherwise the thread could not
171// have been created) and the Packet is Sync because all access to the
172// `UnsafeCell` synchronized (by the `join()` boundary), and `ScopeData` is Sync.
173unsafe impl<'scope, T: Send> Sync for Packet<'scope, T> {}
174
175impl<'scope, T> Drop for Packet<'scope, T> {
176    fn drop(&mut self) {
177        // If this packet was for a thread that ran in a scope, the thread
178        // panicked, and nobody consumed the panic payload, we make sure
179        // the scope function will panic.
180        let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_)));
181        // Drop the result without causing unwinding.
182        // This is only relevant for threads that aren't join()ed, as
183        // join() will take the `result` and set it to None, such that
184        // there is nothing left to drop here.
185        // If this panics, we should handle that, because we're outside the
186        // outermost `catch_unwind` of our thread.
187        // We just abort in that case, since there's nothing else we can do.
188        // (And even if we tried to handle it somehow, we'd also need to handle
189        // the case where the panic payload we get out of it also panics on
190        // drop, and so on. See issue #86027.)
191        if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| {
192            *self.result.get_mut() = None;
193        })) {
194            rtabort!("thread result panicked on drop");
195        }
196        // Book-keeping so the scope knows when it's done.
197        if let Some(scope) = &self.scope {
198            // Now that there will be no more user code running on this thread
199            // that can use 'scope, mark the thread as 'finished'.
200            // It's important we only do this after the `result` has been dropped,
201            // since dropping it might still use things it borrowed from 'scope.
202            scope.decrement_num_running_threads(unhandled_panic);
203        }
204    }
205}
206
207/// Inner representation for JoinHandle
208pub(super) struct JoinInner<'scope, T> {
209    native: imp::Thread,
210    thread: Thread,
211    packet: Arc<Packet<'scope, T>>,
212}
213
214impl<'scope, T> JoinInner<'scope, T> {
215    pub(super) fn is_finished(&self) -> bool {
216        Arc::strong_count(&self.packet) == 1
217    }
218
219    pub(super) fn thread(&self) -> &Thread {
220        &self.thread
221    }
222
223    pub(super) fn join(mut self) -> Result<T> {
224        self.native.join();
225        Arc::get_mut(&mut self.packet)
226            // FIXME(fuzzypixelz): returning an error instead of panicking here
227            // would require updating the documentation of
228            // `std::thread::Result`; currently we can return `Err` if and only
229            // if the thread had panicked.
230            .expect("threads should not terminate unexpectedly")
231            .result
232            .get_mut()
233            .take()
234            .unwrap()
235    }
236}
237
238impl<T> AsInner<imp::Thread> for JoinInner<'static, T> {
239    fn as_inner(&self) -> &imp::Thread {
240        &self.native
241    }
242}
243
244impl<T> IntoInner<imp::Thread> for JoinInner<'static, T> {
245    fn into_inner(self) -> imp::Thread {
246        self.native
247    }
248}