-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Description
First, some terminology. When a thread-local variable gets destroyed there are two possible relevant destructors:
- The
Dropimplementation of the type stored in the thread-local variable. I will call these "user-space" destructors, as they are defined by Rust users. - The cleanup needed by the thread-local implementation to free any related memory. I will call these "internal" destructors, as they are defined by the standard library internals.
I've noticed that on some platforms we leave the moment when TLS variables are destroyed entirely up to the operating system/pthread implementation, for both user-space destructors as well as the internal destructors, whereas on other platforms we have an explicit destructor registry system where user-space destructors are registered to be called during thread clean-up.
I propose we instead always use a registry system for user-space destructors on all platforms, and call them at a well-defined point during thread clean-up. This has several advantages:
- It guarantees that if
needs_drop::<T>()is false for some thread-local variable, thenLocalKey::withcan never fail (assuming the constructor doesn't panic), even when user-space TLS destructors are being ran. This is of potential interest to global allocators, which are regularly called in other user-space destructors. - It can simplify the
std::thread::currentimplementation which currently has to handle the case where its thread-local storage has been cleaned up already, meaning it has to synthesize a newThreadhandle. - Because of (2) we can simplify the
std::thread::Threadhandle implementation to have a single shared canonical handle per thread. This allows us to implement extra functionality which was previously impossible/impractical, such asThread::is_finishedto see if a thread is truly finished just from its handle (which can be shared and passed around, unlikeJoinHandle). - We can strengthen the guarantee of
std::thread::JoinHandle::is_finishedsuch that it only returns true if truly all user-defined Rust code on that thread has finished running - currently it only claimsmainis done.
@joboet Since this is your area of expertise I would be interested to hear your thoughts.