Skip to content

Commit 4a9c3ce

Browse files
committed
Add std::thread::add_spawn_hook.
1 parent bec1029 commit 4a9c3ce

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

library/std/src/thread/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ mod scoped;
185185
#[stable(feature = "scoped_threads", since = "1.63.0")]
186186
pub use scoped::{scope, Scope, ScopedJoinHandle};
187187

188+
mod spawnhook;
189+
190+
#[unstable(feature = "thread_spawn_hook", issue = "none")]
191+
pub use spawnhook::add_spawn_hook;
192+
188193
////////////////////////////////////////////////////////////////////////////////
189194
// Thread-local storage
190195
////////////////////////////////////////////////////////////////////////////////
@@ -492,6 +497,9 @@ impl Builder {
492497
CString::new(name).expect("thread name may not contain interior null bytes"),
493498
)
494499
});
500+
501+
let hooks = spawnhook::run_spawn_hooks(&my_thread)?;
502+
495503
let their_thread = my_thread.clone();
496504

497505
let my_packet: Arc<Packet<'scope, T>> = Arc::new(Packet {
@@ -535,6 +543,9 @@ impl Builder {
535543
}
536544

537545
crate::io::set_output_capture(output_capture);
546+
for hook in hooks {
547+
hook();
548+
}
538549

539550
let f = f.into_inner();
540551
set_current(their_thread);

library/std/src/thread/spawnhook.rs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
use crate::io;
2+
use crate::sync::RwLock;
3+
use crate::thread::Thread;
4+
5+
static SPAWN_HOOKS: RwLock<
6+
Vec<&'static (dyn Fn(&Thread) -> io::Result<Box<dyn FnOnce() + Send>> + Sync)>,
7+
> = RwLock::new(Vec::new());
8+
9+
/// Registers a function to run for every new thread spawned.
10+
///
11+
/// The hook is executed in the parent thread, and returns a function
12+
/// that will be executed in the new thread.
13+
///
14+
/// The hook is called with the `Thread` handle for the new thread.
15+
///
16+
/// If the hook returns an `Err`, thread spawning is aborted. In that case, the
17+
/// function used to spawn the thread (e.g. `std::thread::spawn`) will return
18+
/// the error returned by the hook.
19+
///
20+
/// Hooks can only be added, not removed.
21+
///
22+
/// The hooks will run in order, starting with the most recently added.
23+
///
24+
/// # Usage
25+
///
26+
/// ```
27+
/// #![feature(thread_spawn_hook)]
28+
///
29+
/// std::thread::add_spawn_hook(|_| {
30+
/// ..; // This will run in the parent (spawning) thread.
31+
/// Ok(move || {
32+
/// ..; // This will run it the child (spawned) thread.
33+
/// })
34+
/// });
35+
/// ```
36+
///
37+
/// # Example
38+
///
39+
/// A spawn hook can be used to initialize thread locals from the parent thread:
40+
///
41+
/// ```
42+
/// #![feature(thread_spawn_hook)]
43+
///
44+
/// use std::cell::Cell;
45+
///
46+
/// thread_local! {
47+
/// static X: Cell<u32> = Cell::new(0);
48+
/// }
49+
///
50+
/// std::thread::add_spawn_hook(|_| {
51+
/// // Get the value of X in the spawning thread.
52+
/// let value = X.get();
53+
/// // Set the value of X in the newly spawned thread.
54+
/// Ok(move || {
55+
/// X.set(value);
56+
/// })
57+
/// });
58+
///
59+
/// X.set(123);
60+
///
61+
/// std::thread::spawn(|| {
62+
/// assert_eq!(X.get(), 123);
63+
/// }).join().unwrap();
64+
/// ```
65+
#[unstable(feature = "thread_spawn_hook", issue = "none")]
66+
pub fn add_spawn_hook<F, G>(hook: F)
67+
where
68+
F: 'static + Sync + Fn(&Thread) -> io::Result<G>,
69+
G: 'static + Send + FnOnce(),
70+
{
71+
SPAWN_HOOKS.write().unwrap_or_else(|e| e.into_inner()).push(Box::leak(Box::new(
72+
move |thread: &Thread| -> io::Result<_> {
73+
let f: Box<dyn FnOnce() + Send> = Box::new(hook(thread)?);
74+
Ok(f)
75+
},
76+
)));
77+
}
78+
79+
/// Runs all the spawn hooks.
80+
///
81+
/// Called on the parent thread.
82+
///
83+
/// Returns the functions to be called on the newly spawned thread.
84+
pub(super) fn run_spawn_hooks(thread: &Thread) -> io::Result<Vec<Box<dyn FnOnce() + Send>>> {
85+
SPAWN_HOOKS
86+
.read()
87+
.unwrap_or_else(|e| e.into_inner())
88+
.iter()
89+
.rev()
90+
.map(|hook| hook(thread))
91+
.collect()
92+
}

0 commit comments

Comments
 (0)