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