-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Thread spawn hook (inheriting thread locals) #3642
Conversation
cc @epage - You said you wanted this for your new test framework. :) |
dfbcd39
to
de2c8b3
Compare
de2c8b3
to
2acdaeb
Compare
Implementation, including use in libtest: rust-lang/rust#125405 |
Demonstration: Code#![feature(thread_spawn_hook)]
use std::cell::Cell;
use std::thread;
thread_local! {
static ID: Cell<usize> = panic!("ID not set!");
}
fn main() {
ID.set(123);
thread::add_spawn_hook(|_| {
let id = ID.get();
Ok(move || ID.set(id))
});
thread::spawn(|| {
println!("1: {}", ID.get());
thread::spawn(|| {
println!("1.1: {}", ID.get());
thread::spawn(|| {
println!("1.1.1: {}", ID.get());
}).join().unwrap();
thread::spawn(|| {
println!("1.1.2: {}", ID.get());
}).join().unwrap();
}).join().unwrap();
thread::spawn(|| {
ID.set(456); // <-- change thread local `ID`
println!("1.2: {}", ID.get());
thread::spawn(|| {
println!("1.2.1: {}", ID.get());
}).join().unwrap();
thread::spawn(|| {
println!("1.2.2: {}", ID.get());
}).join().unwrap();
}).join().unwrap();
}).join().unwrap();
thread::spawn(|| {
println!("2: {}", ID.get());
}).join().unwrap();
} Output
|
Could this be made more controllable through |
That might make sense for the stack size, but not for the thread name. And those are (at least today) the only two settings that a I don't think it makes sense to allow 'modifying' the set of hooks. If you want to change/remove any hook, you need a way to identify them. Using a number or string as identifier has many issues, and using some kind of ThreadSpawnHookHandle for each of them seems a bit much, without clear use cases.. |
The default thread name would be empty, as it is today.
The current proposal provides |
Why would you want to fully I strongly believe this should just work like (Even if we were to allow some way to skip the hooks for a specific builder/thread, it doesn't make sense to me to have a global "clear" function to clear all the registered hooks (from the 'global builder' as you propose), because you can't really know which other hooks (of other crates) you might be removing.)
TypeId is not enough. Two hooks might have the same type but a different value (and different behaviour), e.g. with different captures. (Also if you register already boxed |
The RFC already states that threads can be in an unhooked state through pthread spawn. And I assume some weird linkage things (dlopening statically linked plugins?) you could end up with a lot more threads not executing hooks? Additionally I'm somewhat concerned about a tool intended for automatic state inheritance being a global. In the Enterprise Java world such kind of implicit behavior/magic automatisms occasionally get abused to do terribly bloated things which means it's great when that bloat can be limited to a smaller scope or at least be opted out of. In the context of a test framework I can imagine tests running in sets and then clearing global state (or at least enumerating it to print information what didn't get cleaned up) to decouple tests from each other.
"break" in the sense of not doing output capture, as it already is today? Yeah, sure, if the tests need a clean environment for whatever reason that might be a side-effect. |
Today, inheriting the output capturing is done unconditionally when I'm not aware of anyone doing anything specifically to avoid inheriting libtest's output capturing (such as using pthread_create directly for that purpose).
The destructors for e.g. I agree it'd be nice to have some sort of |
I do think we're likely to want deregistration as well, but I think we'd want to do that via a scoped function rather than naming. I don't think it has to be in the initial version in any case. EDIT: On second thought, I'm increasingly concerned about not having the ability to remove these hooks. |
Are there use cases other than testing frameworks in mind? Do they have needs that differ from the testing framework case? |
I think hooks are going to be quite prone to potential deadlocks. That's not a blocker for doing this, just something that'll need to be documented. |
I can think of a few other use cases:
|
TLS drop implementations could likely be used for that. |
It'd be quite unusual to do anything interesting with locks in a hook. They are mainly used with thread locals (e.g. a Cell) rather than globals (e.g. a Mutex). |
Just to advocate for one other use case, I think a proposal like rust-lang/libs-team#195 could be implemented in terms of a feature like this (which would be great!), but I am not sure it's possible in its current form? Since the hook takes a For this reason, I'd argue in favor of something like the aforementioned struct ThreadSpawnHookHandle {
thread: Thread, // or &Thread ?
// possible future extension, or a sys:: abstracted version of this:
#[cfg(unix)]
attrs: libc::pthread_attr_t,
} Then it could be used with something like this: std::thread::add_spawn_hook(|handle| {
// Maybe something like JoinHandleExt:
let attrs = handle.pthread_attr_t_mut();
let my_custom_sched = todo!();
unsafe {
libc::pthread_attr_setschedparam(attrs, &my_custom_sched);
}
|| {}
}); I think having the flexibility to add APIs that affect thread spawning in the future, whether in a standard or OS-specific way, would be worth the trouble of having the (relatively small) inconvenience of this wrapper type, if there are no other reasons |
@ian-h-chamberlain Yeah, that possibility is mentioned in one of the unresolved questions in the RFC:
|
We discussed this in the libs-api meeting; this seems ready for FCP. @rfcbot merge |
Team member @m-ou-se has proposed to merge this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
We discussed this in the libs-api meeting today and we're happier with the design now that it doesn't involve global state any more. @rfcbot fcp merge |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
You could use |
Yeah, but one of the motivations for rust-lang/libs-team#195 was to support platforms that don't allow setting these kinds of parameters for the main thread /current thread (i.e. must be set on the attributes used to spawn the thread). My main concern with the proposed API as written is that there won't be any way to extend it to support those use cases in the future, without some invasive changes to how |
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
Tracking issue: rust-lang/rust#132951 |
…affleLapkin Add std::thread::add_spawn_hook. Implementation of rust-lang/rfcs#3642
Rollup merge of rust-lang#125405 - m-ou-se:thread-add-spawn-hook, r=WaffleLapkin Add std::thread::add_spawn_hook. Implementation of rust-lang/rfcs#3642
Add std::thread::add_spawn_hook. Implementation of rust-lang/rfcs#3642
Rendered