2024-10-24 10:52:47 +02:00
|
|
|
use crate::cell::Cell;
|
|
|
|
|
use crate::sync::Arc;
|
2024-05-22 14:10:52 +02:00
|
|
|
use crate::thread::Thread;
|
|
|
|
|
|
2024-10-24 10:52:47 +02:00
|
|
|
// A thread local linked list of spawn hooks.
|
|
|
|
|
crate::thread_local! {
|
|
|
|
|
static SPAWN_HOOKS: Cell<SpawnHooks> = const { Cell::new(SpawnHooks { first: None }) };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Default, Clone)]
|
|
|
|
|
struct SpawnHooks {
|
|
|
|
|
first: Option<Arc<SpawnHook>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Manually implement drop to prevent deep recursion when dropping linked Arc list.
|
|
|
|
|
impl Drop for SpawnHooks {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
let mut next = self.first.take();
|
|
|
|
|
while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) {
|
|
|
|
|
drop(hook);
|
|
|
|
|
next = n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SpawnHook {
|
2024-10-24 11:37:35 +02:00
|
|
|
hook: Box<dyn Send + Sync + Fn(&Thread) -> Box<dyn Send + FnOnce()>>,
|
2024-10-24 10:52:47 +02:00
|
|
|
next: Option<Arc<SpawnHook>>,
|
|
|
|
|
}
|
2024-05-22 14:10:52 +02:00
|
|
|
|
2024-10-24 10:52:47 +02:00
|
|
|
/// Registers a function to run for every newly thread spawned.
|
2024-05-22 14:10:52 +02:00
|
|
|
///
|
|
|
|
|
/// The hook is executed in the parent thread, and returns a function
|
|
|
|
|
/// that will be executed in the new thread.
|
|
|
|
|
///
|
|
|
|
|
/// The hook is called with the `Thread` handle for the new thread.
|
|
|
|
|
///
|
2024-10-24 10:52:47 +02:00
|
|
|
/// The hook will only be added for the current thread and is inherited by the threads it spawns.
|
|
|
|
|
/// In other words, adding a hook has no effect on already running threads (other than the current
|
|
|
|
|
/// thread) and the threads they might spawn in the future.
|
2024-05-22 14:10:52 +02:00
|
|
|
///
|
|
|
|
|
/// Hooks can only be added, not removed.
|
|
|
|
|
///
|
|
|
|
|
/// The hooks will run in order, starting with the most recently added.
|
|
|
|
|
///
|
|
|
|
|
/// # Usage
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// #![feature(thread_spawn_hook)]
|
|
|
|
|
///
|
|
|
|
|
/// std::thread::add_spawn_hook(|_| {
|
|
|
|
|
/// ..; // This will run in the parent (spawning) thread.
|
2024-10-24 10:52:47 +02:00
|
|
|
/// move || {
|
2024-05-22 14:10:52 +02:00
|
|
|
/// ..; // This will run it the child (spawned) thread.
|
2024-10-24 10:52:47 +02:00
|
|
|
/// }
|
2024-05-22 14:10:52 +02:00
|
|
|
/// });
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
///
|
2024-10-24 10:52:47 +02:00
|
|
|
/// A spawn hook can be used to "inherit" a thread local from the parent thread:
|
2024-05-22 14:10:52 +02:00
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// #![feature(thread_spawn_hook)]
|
|
|
|
|
///
|
|
|
|
|
/// use std::cell::Cell;
|
|
|
|
|
///
|
|
|
|
|
/// thread_local! {
|
|
|
|
|
/// static X: Cell<u32> = Cell::new(0);
|
|
|
|
|
/// }
|
|
|
|
|
///
|
2024-10-24 10:52:47 +02:00
|
|
|
/// // This needs to be done once in the main thread before spawning any threads.
|
2024-05-22 14:10:52 +02:00
|
|
|
/// std::thread::add_spawn_hook(|_| {
|
|
|
|
|
/// // Get the value of X in the spawning thread.
|
|
|
|
|
/// let value = X.get();
|
|
|
|
|
/// // Set the value of X in the newly spawned thread.
|
2024-10-24 10:52:47 +02:00
|
|
|
/// move || X.set(value)
|
2024-05-22 14:10:52 +02:00
|
|
|
/// });
|
|
|
|
|
///
|
|
|
|
|
/// X.set(123);
|
|
|
|
|
///
|
|
|
|
|
/// std::thread::spawn(|| {
|
|
|
|
|
/// assert_eq!(X.get(), 123);
|
|
|
|
|
/// }).join().unwrap();
|
|
|
|
|
/// ```
|
2024-11-19 15:19:13 +01:00
|
|
|
#[unstable(feature = "thread_spawn_hook", issue = "132951")]
|
2024-05-22 14:10:52 +02:00
|
|
|
pub fn add_spawn_hook<F, G>(hook: F)
|
|
|
|
|
where
|
2024-10-24 11:37:35 +02:00
|
|
|
F: 'static + Send + Sync + Fn(&Thread) -> G,
|
2024-05-22 14:10:52 +02:00
|
|
|
G: 'static + Send + FnOnce(),
|
|
|
|
|
{
|
2024-10-24 10:52:47 +02:00
|
|
|
SPAWN_HOOKS.with(|h| {
|
|
|
|
|
let mut hooks = h.take();
|
|
|
|
|
hooks.first = Some(Arc::new(SpawnHook {
|
|
|
|
|
hook: Box::new(move |thread| Box::new(hook(thread))),
|
|
|
|
|
next: hooks.first.take(),
|
|
|
|
|
}));
|
|
|
|
|
h.set(hooks);
|
|
|
|
|
});
|
2024-05-22 14:10:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Runs all the spawn hooks.
|
|
|
|
|
///
|
|
|
|
|
/// Called on the parent thread.
|
|
|
|
|
///
|
|
|
|
|
/// Returns the functions to be called on the newly spawned thread.
|
2024-10-24 11:19:02 +02:00
|
|
|
pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks {
|
2024-10-24 10:52:47 +02:00
|
|
|
// Get a snapshot of the spawn hooks.
|
|
|
|
|
// (Increments the refcount to the first node.)
|
|
|
|
|
let hooks = SPAWN_HOOKS.with(|hooks| {
|
|
|
|
|
let snapshot = hooks.take();
|
|
|
|
|
hooks.set(snapshot.clone());
|
|
|
|
|
snapshot
|
|
|
|
|
});
|
|
|
|
|
// Iterate over the hooks, run them, and collect the results in a vector.
|
|
|
|
|
let mut next: &Option<Arc<SpawnHook>> = &hooks.first;
|
|
|
|
|
let mut to_run = Vec::new();
|
|
|
|
|
while let Some(hook) = next {
|
|
|
|
|
to_run.push((hook.hook)(thread));
|
|
|
|
|
next = &hook.next;
|
|
|
|
|
}
|
|
|
|
|
// Pass on the snapshot of the hooks and the results to the new thread,
|
|
|
|
|
// which will then run SpawnHookResults::run().
|
2024-10-24 11:19:02 +02:00
|
|
|
ChildSpawnHooks { hooks, to_run }
|
2024-10-24 10:52:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The results of running the spawn hooks.
|
|
|
|
|
///
|
|
|
|
|
/// This struct is sent to the new thread.
|
|
|
|
|
/// It contains the inherited hooks and the closures to be run.
|
2024-10-24 11:19:02 +02:00
|
|
|
#[derive(Default)]
|
|
|
|
|
pub(super) struct ChildSpawnHooks {
|
2024-10-24 10:52:47 +02:00
|
|
|
hooks: SpawnHooks,
|
|
|
|
|
to_run: Vec<Box<dyn FnOnce() + Send>>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-24 11:19:02 +02:00
|
|
|
impl ChildSpawnHooks {
|
2024-10-24 10:52:47 +02:00
|
|
|
// This is run on the newly spawned thread, directly at the start.
|
|
|
|
|
pub(super) fn run(self) {
|
|
|
|
|
SPAWN_HOOKS.set(self.hooks);
|
|
|
|
|
for run in self.to_run {
|
|
|
|
|
run();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-22 14:10:52 +02:00
|
|
|
}
|