Skip to content

Commit 5bce305

Browse files
authored
Rollup merge of rust-lang#148765 - joboet:split-up-thread, r=ChrisDenton
std: split up the `thread` module Almost all functionality in `std::thread` is currently implemented in `thread/mod.rs`, resulting in a *huge* file with more than 2000 lines and multiple, interoperating `unsafe` sections. This PR splits the file up into multiple different private modules, each implementing mostly independent parts of the functionality. The only remaining `unsafe` interplay is that of the `lifecycle` and `scope` modules, the `spawn_scoped` implementation relies on the live thread count being updated correctly by the `lifecycle` module. This PR contains no functional changes and only moves code around for the most part, with a few notable exceptions: * `with_current_name` is moved to the already existing `current` module and now uses the `name` method instead of calculating the name from private fields. The old code was just a reimplementation of that method anyway. * The private `JoinInner` type used to implement both join handles now has some more methods (`is_finished`, `thread` and the `AsInner`/`IntoInner` implementations) to avoid having to expose private fields and their invariants. * The private `spawn_unchecked_` (note the underscore) method of `Builder` is now a freestanding function in `lifecycle`. The rest of the changes are just visibility annotations. I realise this PR ended up quite large – let me know if there is anyway I can aid the review process. Edit: I've simplified the diff by adding an intermediate commit that creates all the new files by duplicating `mod.rs`. The actual changes in the second commit thus appear to delete the non-relevant parts from the respective file.
2 parents 83925bd + 693055f commit 5bce305

29 files changed

+1971
-1906
lines changed

library/std/src/thread/builder.rs

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
use super::join_handle::JoinHandle;
2+
use super::lifecycle::spawn_unchecked;
3+
use crate::io;
4+
5+
/// Thread factory, which can be used in order to configure the properties of
6+
/// a new thread.
7+
///
8+
/// Methods can be chained on it in order to configure it.
9+
///
10+
/// The two configurations available are:
11+
///
12+
/// - [`name`]: specifies an [associated name for the thread][naming-threads]
13+
/// - [`stack_size`]: specifies the [desired stack size for the thread][stack-size]
14+
///
15+
/// The [`spawn`] method will take ownership of the builder and create an
16+
/// [`io::Result`] to the thread handle with the given configuration.
17+
///
18+
/// The [`thread::spawn`] free function uses a `Builder` with default
19+
/// configuration and [`unwrap`]s its return value.
20+
///
21+
/// You may want to use [`spawn`] instead of [`thread::spawn`], when you want
22+
/// to recover from a failure to launch a thread, indeed the free function will
23+
/// panic where the `Builder` method will return a [`io::Result`].
24+
///
25+
/// # Examples
26+
///
27+
/// ```
28+
/// use std::thread;
29+
///
30+
/// let builder = thread::Builder::new();
31+
///
32+
/// let handler = builder.spawn(|| {
33+
/// // thread code
34+
/// }).unwrap();
35+
///
36+
/// handler.join().unwrap();
37+
/// ```
38+
///
39+
/// [`stack_size`]: Builder::stack_size
40+
/// [`name`]: Builder::name
41+
/// [`spawn`]: Builder::spawn
42+
/// [`thread::spawn`]: super::spawn
43+
/// [`io::Result`]: crate::io::Result
44+
/// [`unwrap`]: crate::result::Result::unwrap
45+
/// [naming-threads]: ./index.html#naming-threads
46+
/// [stack-size]: ./index.html#stack-size
47+
#[must_use = "must eventually spawn the thread"]
48+
#[stable(feature = "rust1", since = "1.0.0")]
49+
#[derive(Debug)]
50+
pub struct Builder {
51+
/// A name for the thread-to-be, for identification in panic messages
52+
pub(super) name: Option<String>,
53+
/// The size of the stack for the spawned thread in bytes
54+
pub(super) stack_size: Option<usize>,
55+
/// Skip running and inheriting the thread spawn hooks
56+
pub(super) no_hooks: bool,
57+
}
58+
59+
impl Builder {
60+
/// Generates the base configuration for spawning a thread, from which
61+
/// configuration methods can be chained.
62+
///
63+
/// # Examples
64+
///
65+
/// ```
66+
/// use std::thread;
67+
///
68+
/// let builder = thread::Builder::new()
69+
/// .name("foo".into())
70+
/// .stack_size(32 * 1024);
71+
///
72+
/// let handler = builder.spawn(|| {
73+
/// // thread code
74+
/// }).unwrap();
75+
///
76+
/// handler.join().unwrap();
77+
/// ```
78+
#[stable(feature = "rust1", since = "1.0.0")]
79+
pub fn new() -> Builder {
80+
Builder { name: None, stack_size: None, no_hooks: false }
81+
}
82+
83+
/// Names the thread-to-be. Currently the name is used for identification
84+
/// only in panic messages.
85+
///
86+
/// The name must not contain null bytes (`\0`).
87+
///
88+
/// For more information about named threads, see
89+
/// [this module-level documentation][naming-threads].
90+
///
91+
/// # Examples
92+
///
93+
/// ```
94+
/// use std::thread;
95+
///
96+
/// let builder = thread::Builder::new()
97+
/// .name("foo".into());
98+
///
99+
/// let handler = builder.spawn(|| {
100+
/// assert_eq!(thread::current().name(), Some("foo"))
101+
/// }).unwrap();
102+
///
103+
/// handler.join().unwrap();
104+
/// ```
105+
///
106+
/// [naming-threads]: ./index.html#naming-threads
107+
#[stable(feature = "rust1", since = "1.0.0")]
108+
pub fn name(mut self, name: String) -> Builder {
109+
self.name = Some(name);
110+
self
111+
}
112+
113+
/// Sets the size of the stack (in bytes) for the new thread.
114+
///
115+
/// The actual stack size may be greater than this value if
116+
/// the platform specifies a minimal stack size.
117+
///
118+
/// For more information about the stack size for threads, see
119+
/// [this module-level documentation][stack-size].
120+
///
121+
/// # Examples
122+
///
123+
/// ```
124+
/// use std::thread;
125+
///
126+
/// let builder = thread::Builder::new().stack_size(32 * 1024);
127+
/// ```
128+
///
129+
/// [stack-size]: ./index.html#stack-size
130+
#[stable(feature = "rust1", since = "1.0.0")]
131+
pub fn stack_size(mut self, size: usize) -> Builder {
132+
self.stack_size = Some(size);
133+
self
134+
}
135+
136+
/// Disables running and inheriting [spawn hooks].
137+
///
138+
/// Use this if the parent thread is in no way relevant for the child thread.
139+
/// For example, when lazily spawning threads for a thread pool.
140+
///
141+
/// [spawn hooks]: super::add_spawn_hook
142+
#[unstable(feature = "thread_spawn_hook", issue = "132951")]
143+
pub fn no_hooks(mut self) -> Builder {
144+
self.no_hooks = true;
145+
self
146+
}
147+
148+
/// Spawns a new thread by taking ownership of the `Builder`, and returns an
149+
/// [`io::Result`] to its [`JoinHandle`].
150+
///
151+
/// The spawned thread may outlive the caller (unless the caller thread
152+
/// is the main thread; the whole process is terminated when the main
153+
/// thread finishes). The join handle can be used to block on
154+
/// termination of the spawned thread, including recovering its panics.
155+
///
156+
/// For a more complete documentation see [`thread::spawn`].
157+
///
158+
/// # Errors
159+
///
160+
/// Unlike the [`spawn`] free function, this method yields an
161+
/// [`io::Result`] to capture any failure to create the thread at
162+
/// the OS level.
163+
///
164+
/// [`io::Result`]: crate::io::Result
165+
///
166+
/// # Panics
167+
///
168+
/// Panics if a thread name was set and it contained null bytes.
169+
///
170+
/// # Examples
171+
///
172+
/// ```
173+
/// use std::thread;
174+
///
175+
/// let builder = thread::Builder::new();
176+
///
177+
/// let handler = builder.spawn(|| {
178+
/// // thread code
179+
/// }).unwrap();
180+
///
181+
/// handler.join().unwrap();
182+
/// ```
183+
///
184+
/// [`thread::spawn`]: super::spawn
185+
/// [`spawn`]: super::spawn
186+
#[stable(feature = "rust1", since = "1.0.0")]
187+
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
188+
pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
189+
where
190+
F: FnOnce() -> T,
191+
F: Send + 'static,
192+
T: Send + 'static,
193+
{
194+
unsafe { self.spawn_unchecked(f) }
195+
}
196+
197+
/// Spawns a new thread without any lifetime restrictions by taking ownership
198+
/// of the `Builder`, and returns an [`io::Result`] to its [`JoinHandle`].
199+
///
200+
/// The spawned thread may outlive the caller (unless the caller thread
201+
/// is the main thread; the whole process is terminated when the main
202+
/// thread finishes). The join handle can be used to block on
203+
/// termination of the spawned thread, including recovering its panics.
204+
///
205+
/// This method is identical to [`thread::Builder::spawn`][`Builder::spawn`],
206+
/// except for the relaxed lifetime bounds, which render it unsafe.
207+
/// For a more complete documentation see [`thread::spawn`].
208+
///
209+
/// # Errors
210+
///
211+
/// Unlike the [`spawn`] free function, this method yields an
212+
/// [`io::Result`] to capture any failure to create the thread at
213+
/// the OS level.
214+
///
215+
/// # Panics
216+
///
217+
/// Panics if a thread name was set and it contained null bytes.
218+
///
219+
/// # Safety
220+
///
221+
/// The caller has to ensure that the spawned thread does not outlive any
222+
/// references in the supplied thread closure and its return type.
223+
/// This can be guaranteed in two ways:
224+
///
225+
/// - ensure that [`join`][`JoinHandle::join`] is called before any referenced
226+
/// data is dropped
227+
/// - use only types with `'static` lifetime bounds, i.e., those with no or only
228+
/// `'static` references (both [`thread::Builder::spawn`][`Builder::spawn`]
229+
/// and [`thread::spawn`] enforce this property statically)
230+
///
231+
/// # Examples
232+
///
233+
/// ```
234+
/// use std::thread;
235+
///
236+
/// let builder = thread::Builder::new();
237+
///
238+
/// let x = 1;
239+
/// let thread_x = &x;
240+
///
241+
/// let handler = unsafe {
242+
/// builder.spawn_unchecked(move || {
243+
/// println!("x = {}", *thread_x);
244+
/// }).unwrap()
245+
/// };
246+
///
247+
/// // caller has to ensure `join()` is called, otherwise
248+
/// // it is possible to access freed memory if `x` gets
249+
/// // dropped before the thread closure is executed!
250+
/// handler.join().unwrap();
251+
/// ```
252+
///
253+
/// [`io::Result`]: crate::io::Result
254+
/// [`thread::spawn`]: super::spawn
255+
/// [`spawn`]: super::spawn
256+
#[stable(feature = "thread_spawn_unchecked", since = "1.82.0")]
257+
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
258+
pub unsafe fn spawn_unchecked<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
259+
where
260+
F: FnOnce() -> T,
261+
F: Send,
262+
T: Send,
263+
{
264+
let Builder { name, stack_size, no_hooks } = self;
265+
Ok(JoinHandle(unsafe { spawn_unchecked(name, stack_size, no_hooks, None, f) }?))
266+
}
267+
}

library/std/src/thread/current.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use super::{Thread, ThreadId, imp};
1+
use super::id::ThreadId;
2+
use super::main_thread;
3+
use super::thread::Thread;
24
use crate::mem::ManuallyDrop;
35
use crate::ptr;
6+
use crate::sys::thread as imp;
47
use crate::sys::thread_local::local_pointer;
58

69
const NONE: *mut () = ptr::null_mut();
@@ -184,7 +187,7 @@ pub(crate) fn current_os_id() -> u64 {
184187

185188
/// Gets a reference to the handle of the thread that invokes it, if the handle
186189
/// has been initialized.
187-
pub(super) fn try_with_current<F, R>(f: F) -> R
190+
fn try_with_current<F, R>(f: F) -> R
188191
where
189192
F: FnOnce(Option<&Thread>) -> R,
190193
{
@@ -202,6 +205,36 @@ where
202205
}
203206
}
204207

208+
/// Run a function with the current thread's name.
209+
///
210+
/// Modulo thread local accesses, this function is safe to call from signal
211+
/// handlers and in similar circumstances where allocations are not possible.
212+
pub(crate) fn with_current_name<F, R>(f: F) -> R
213+
where
214+
F: FnOnce(Option<&str>) -> R,
215+
{
216+
try_with_current(|thread| {
217+
let name = if let Some(thread) = thread {
218+
// If there is a current thread handle, try to use the name stored
219+
// there.
220+
thread.name()
221+
} else if let Some(main) = main_thread::get()
222+
&& let Some(id) = id::get()
223+
&& id == main
224+
{
225+
// The main thread doesn't always have a thread handle, we must
226+
// identify it through its ID instead. The checks are ordered so
227+
// that the current ID is only loaded if it is actually needed,
228+
// since loading it from TLS might need multiple expensive accesses.
229+
Some("main")
230+
} else {
231+
None
232+
};
233+
234+
f(name)
235+
})
236+
}
237+
205238
/// Gets a handle to the thread that invokes it. If the handle stored in thread-
206239
/// local storage was already destroyed, this creates a new unnamed temporary
207240
/// handle to allow thread parking in nearly all situations.

0 commit comments

Comments
 (0)