1 | use crate::cell::Cell; |
2 | use crate::iter; |
3 | use crate::sync::Arc; |
4 | use crate::thread::Thread; |
5 | |
6 | crate::thread_local! { |
7 | /// A thread local linked list of spawn hooks. |
8 | /// |
9 | /// It is a linked list of Arcs, such that it can very cheaply be inhereted by spawned threads. |
10 | /// |
11 | /// (That technically makes it a set of linked lists with shared tails, so a linked tree.) |
12 | static SPAWN_HOOKS: Cell<SpawnHooks> = const { Cell::new(SpawnHooks { first: None }) }; |
13 | } |
14 | |
15 | #[derive (Default, Clone)] |
16 | struct SpawnHooks { |
17 | first: Option<Arc<SpawnHook>>, |
18 | } |
19 | |
20 | // Manually implement drop to prevent deep recursion when dropping linked Arc list. |
21 | impl Drop for SpawnHooks { |
22 | fn drop(&mut self) { |
23 | let mut next: Option> = self.first.take(); |
24 | while let Some(SpawnHook { hook: Box Box<…> + Send + Sync + 'static>, next: n: Option> }) = next.and_then(|n: Arc| Arc::into_inner(this:n)) { |
25 | drop(hook); |
26 | next = n; |
27 | } |
28 | } |
29 | } |
30 | |
31 | struct SpawnHook { |
32 | hook: Box<dyn Send + Sync + Fn(&Thread) -> Box<dyn Send + FnOnce()>>, |
33 | next: Option<Arc<SpawnHook>>, |
34 | } |
35 | |
36 | /// Registers a function to run for every newly thread spawned. |
37 | /// |
38 | /// The hook is executed in the parent thread, and returns a function |
39 | /// that will be executed in the new thread. |
40 | /// |
41 | /// The hook is called with the `Thread` handle for the new thread. |
42 | /// |
43 | /// The hook will only be added for the current thread and is inherited by the threads it spawns. |
44 | /// In other words, adding a hook has no effect on already running threads (other than the current |
45 | /// thread) and the threads they might spawn in the future. |
46 | /// |
47 | /// Hooks can only be added, not removed. |
48 | /// |
49 | /// The hooks will run in reverse order, starting with the most recently added. |
50 | /// |
51 | /// # Usage |
52 | /// |
53 | /// ``` |
54 | /// #![feature(thread_spawn_hook)] |
55 | /// |
56 | /// std::thread::add_spawn_hook(|_| { |
57 | /// ..; // This will run in the parent (spawning) thread. |
58 | /// move || { |
59 | /// ..; // This will run it the child (spawned) thread. |
60 | /// } |
61 | /// }); |
62 | /// ``` |
63 | /// |
64 | /// # Example |
65 | /// |
66 | /// A spawn hook can be used to "inherit" a thread local from the parent thread: |
67 | /// |
68 | /// ``` |
69 | /// #![feature(thread_spawn_hook)] |
70 | /// |
71 | /// use std::cell::Cell; |
72 | /// |
73 | /// thread_local! { |
74 | /// static X: Cell<u32> = Cell::new(0); |
75 | /// } |
76 | /// |
77 | /// // This needs to be done once in the main thread before spawning any threads. |
78 | /// std::thread::add_spawn_hook(|_| { |
79 | /// // Get the value of X in the spawning thread. |
80 | /// let value = X.get(); |
81 | /// // Set the value of X in the newly spawned thread. |
82 | /// move || X.set(value) |
83 | /// }); |
84 | /// |
85 | /// X.set(123); |
86 | /// |
87 | /// std::thread::spawn(|| { |
88 | /// assert_eq!(X.get(), 123); |
89 | /// }).join().unwrap(); |
90 | /// ``` |
91 | #[unstable (feature = "thread_spawn_hook" , issue = "132951" )] |
92 | pub fn add_spawn_hook<F, G>(hook: F) |
93 | where |
94 | F: 'static + Send + Sync + Fn(&Thread) -> G, |
95 | G: 'static + Send + FnOnce(), |
96 | { |
97 | SPAWN_HOOKS.with(|h: &Cell| { |
98 | let mut hooks: SpawnHooks = h.take(); |
99 | let next: Option> = hooks.first.take(); |
100 | hooks.first = Some(Arc::new(data:SpawnHook { |
101 | hook: Box::new(move |thread: &Thread| Box::new(hook(thread))), |
102 | next, |
103 | })); |
104 | h.set(val:hooks); |
105 | }); |
106 | } |
107 | |
108 | /// Runs all the spawn hooks. |
109 | /// |
110 | /// Called on the parent thread. |
111 | /// |
112 | /// Returns the functions to be called on the newly spawned thread. |
113 | pub(super) fn run_spawn_hooks(thread: &Thread) -> ChildSpawnHooks { |
114 | // Get a snapshot of the spawn hooks. |
115 | // (Increments the refcount to the first node.) |
116 | if let Ok(hooks: SpawnHooks) = SPAWN_HOOKS.try_with(|hooks: &Cell| { |
117 | let snapshot: SpawnHooks = hooks.take(); |
118 | hooks.set(val:snapshot.clone()); |
119 | snapshot |
120 | }) { |
121 | // Iterate over the hooks, run them, and collect the results in a vector. |
122 | let to_run: Vec<_> = iterimpl Iterator- >
::successors(first:hooks.first.as_deref(), |hook: &&SpawnHook| hook.next.as_deref()) |
123 | .map(|hook: &SpawnHook| (hook.hook)(thread)) |
124 | .collect(); |
125 | // Pass on the snapshot of the hooks and the results to the new thread, |
126 | // which will then run SpawnHookResults::run(). |
127 | ChildSpawnHooks { hooks, to_run } |
128 | } else { |
129 | // TLS has been destroyed. Skip running the hooks. |
130 | // See https://github.com/rust-lang/rust/issues/138696 |
131 | ChildSpawnHooks::default() |
132 | } |
133 | } |
134 | |
135 | /// The results of running the spawn hooks. |
136 | /// |
137 | /// This struct is sent to the new thread. |
138 | /// It contains the inherited hooks and the closures to be run. |
139 | #[derive (Default)] |
140 | pub(super) struct ChildSpawnHooks { |
141 | hooks: SpawnHooks, |
142 | to_run: Vec<Box<dyn FnOnce() + Send>>, |
143 | } |
144 | |
145 | impl ChildSpawnHooks { |
146 | // This is run on the newly spawned thread, directly at the start. |
147 | pub(super) fn run(self) { |
148 | SPAWN_HOOKS.set(self.hooks); |
149 | for run: Box in self.to_run { |
150 | run(); |
151 | } |
152 | } |
153 | } |
154 | |