Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 49ac15b

Browse filesBrowse files
committed
Update thread spawn hooks.
1. Make the effect thread local. 2. Don't return a io::Result from hooks.
1 parent 2cc4b2e commit 49ac15b
Copy full SHA for 49ac15b

File tree

Expand file treeCollapse file tree

3 files changed

+86
-37
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+86
-37
lines changed

‎std/src/thread/mod.rs

Copy file name to clipboardExpand all lines: std/src/thread/mod.rs
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ impl Builder {
491491
None => Thread::new_unnamed(id),
492492
};
493493

494-
let hooks = spawnhook::run_spawn_hooks(&my_thread)?;
494+
let hooks = spawnhook::run_spawn_hooks(&my_thread);
495495

496496
let their_thread = my_thread.clone();
497497

@@ -539,12 +539,9 @@ impl Builder {
539539
imp::Thread::set_name(name);
540540
}
541541

542-
for hook in hooks {
543-
hook();
544-
}
545-
546542
let f = f.into_inner();
547543
let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
544+
crate::sys::backtrace::__rust_begin_short_backtrace(|| hooks.run());
548545
crate::sys::backtrace::__rust_begin_short_backtrace(f)
549546
}));
550547
// SAFETY: `their_packet` as been built just above and moved by the

‎std/src/thread/spawnhook.rs

Copy file name to clipboard
+82-30Lines changed: 82 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
1-
use crate::io;
2-
use crate::sync::RwLock;
1+
use crate::cell::Cell;
2+
use crate::sync::Arc;
33
use crate::thread::Thread;
44

5-
static SPAWN_HOOKS: RwLock<
6-
Vec<&'static (dyn Fn(&Thread) -> io::Result<Box<dyn FnOnce() + Send>> + Sync)>,
7-
> = RwLock::new(Vec::new());
5+
// A thread local linked list of spawn hooks.
6+
crate::thread_local! {
7+
static SPAWN_HOOKS: Cell<SpawnHooks> = const { Cell::new(SpawnHooks { first: None }) };
8+
}
9+
10+
#[derive(Default, Clone)]
11+
struct SpawnHooks {
12+
first: Option<Arc<SpawnHook>>,
13+
}
14+
15+
// Manually implement drop to prevent deep recursion when dropping linked Arc list.
16+
impl Drop for SpawnHooks {
17+
fn drop(&mut self) {
18+
let mut next = self.first.take();
19+
while let Some(SpawnHook { hook, next: n }) = next.and_then(|n| Arc::into_inner(n)) {
20+
drop(hook);
21+
next = n;
22+
}
23+
}
24+
}
25+
26+
struct SpawnHook {
27+
hook: Box<dyn Sync + Fn(&Thread) -> Box<dyn Send + FnOnce()>>,
28+
next: Option<Arc<SpawnHook>>,
29+
}
830

9-
/// Registers a function to run for every new thread spawned.
31+
/// Registers a function to run for every newly thread spawned.
1032
///
1133
/// The hook is executed in the parent thread, and returns a function
1234
/// that will be executed in the new thread.
1335
///
1436
/// The hook is called with the `Thread` handle for the new thread.
1537
///
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.
38+
/// The hook will only be added for the current thread and is inherited by the threads it spawns.
39+
/// In other words, adding a hook has no effect on already running threads (other than the current
40+
/// thread) and the threads they might spawn in the future.
1941
///
2042
/// Hooks can only be added, not removed.
2143
///
@@ -28,15 +50,15 @@ static SPAWN_HOOKS: RwLock<
2850
///
2951
/// std::thread::add_spawn_hook(|_| {
3052
/// ..; // This will run in the parent (spawning) thread.
31-
/// Ok(move || {
53+
/// move || {
3254
/// ..; // This will run it the child (spawned) thread.
33-
/// })
55+
/// }
3456
/// });
3557
/// ```
3658
///
3759
/// # Example
3860
///
39-
/// A spawn hook can be used to initialize thread locals from the parent thread:
61+
/// A spawn hook can be used to "inherit" a thread local from the parent thread:
4062
///
4163
/// ```
4264
/// #![feature(thread_spawn_hook)]
@@ -47,13 +69,12 @@ static SPAWN_HOOKS: RwLock<
4769
/// static X: Cell<u32> = Cell::new(0);
4870
/// }
4971
///
72+
/// // This needs to be done once in the main thread before spawning any threads.
5073
/// std::thread::add_spawn_hook(|_| {
5174
/// // Get the value of X in the spawning thread.
5275
/// let value = X.get();
5376
/// // Set the value of X in the newly spawned thread.
54-
/// Ok(move || {
55-
/// X.set(value);
56-
/// })
77+
/// move || X.set(value)
5778
/// });
5879
///
5980
/// X.set(123);
@@ -65,28 +86,59 @@ static SPAWN_HOOKS: RwLock<
6586
#[unstable(feature = "thread_spawn_hook", issue = "none")]
6687
pub fn add_spawn_hook<F, G>(hook: F)
6788
where
68-
F: 'static + Sync + Fn(&Thread) -> io::Result<G>,
89+
F: 'static + Sync + Fn(&Thread) -> G,
6990
G: 'static + Send + FnOnce(),
7091
{
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-
)));
92+
SPAWN_HOOKS.with(|h| {
93+
let mut hooks = h.take();
94+
hooks.first = Some(Arc::new(SpawnHook {
95+
hook: Box::new(move |thread| Box::new(hook(thread))),
96+
next: hooks.first.take(),
97+
}));
98+
h.set(hooks);
99+
});
77100
}
78101

79102
/// Runs all the spawn hooks.
80103
///
81104
/// Called on the parent thread.
82105
///
83106
/// 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()
107+
pub(super) fn run_spawn_hooks(thread: &Thread) -> SpawnHookResults {
108+
// Get a snapshot of the spawn hooks.
109+
// (Increments the refcount to the first node.)
110+
let hooks = SPAWN_HOOKS.with(|hooks| {
111+
let snapshot = hooks.take();
112+
hooks.set(snapshot.clone());
113+
snapshot
114+
});
115+
// Iterate over the hooks, run them, and collect the results in a vector.
116+
let mut next: &Option<Arc<SpawnHook>> = &hooks.first;
117+
let mut to_run = Vec::new();
118+
while let Some(hook) = next {
119+
to_run.push((hook.hook)(thread));
120+
next = &hook.next;
121+
}
122+
// Pass on the snapshot of the hooks and the results to the new thread,
123+
// which will then run SpawnHookResults::run().
124+
SpawnHookResults { hooks, to_run }
125+
}
126+
127+
/// The results of running the spawn hooks.
128+
///
129+
/// This struct is sent to the new thread.
130+
/// It contains the inherited hooks and the closures to be run.
131+
pub(super) struct SpawnHookResults {
132+
hooks: SpawnHooks,
133+
to_run: Vec<Box<dyn FnOnce() + Send>>,
134+
}
135+
136+
impl SpawnHookResults {
137+
// This is run on the newly spawned thread, directly at the start.
138+
pub(super) fn run(self) {
139+
SPAWN_HOOKS.set(self.hooks);
140+
for run in self.to_run {
141+
run();
142+
}
143+
}
92144
}

‎test/src/lib.rs

Copy file name to clipboardExpand all lines: test/src/lib.rs
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,9 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
141141
let output_capture = io::set_output_capture(None);
142142
io::set_output_capture(output_capture.clone());
143143
// Set the output capture of the new thread.
144-
Ok(|| {
144+
|| {
145145
io::set_output_capture(output_capture);
146-
})
146+
}
147147
});
148148
}
149149
let res = console::run_tests_console(&opts, tests);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.