diff options
Diffstat (limited to 'library/std/src/sys/thread_local/mod.rs')
| -rw-r--r-- | library/std/src/sys/thread_local/mod.rs | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs new file mode 100644 index 00000000000..8b2c839f837 --- /dev/null +++ b/library/std/src/sys/thread_local/mod.rs @@ -0,0 +1,124 @@ +#![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")] + +// There are three thread-local implementations: "static", "fast", "OS". +// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the +// "fast" key type is accessed via code generated via LLVM, where TLS keys are set up by the linker. +// "static" is for single-threaded platforms where a global static is sufficient. + +cfg_if::cfg_if! { + if #[cfg(any(all(target_family = "wasm", not(target_feature = "atomics")), target_os = "uefi"))] { + #[doc(hidden)] + mod static_local; + #[doc(hidden)] + pub use static_local::{Key, thread_local_inner}; + } else if #[cfg(target_thread_local)] { + #[doc(hidden)] + mod fast_local; + #[doc(hidden)] + pub use fast_local::{Key, thread_local_inner}; + } else { + #[doc(hidden)] + mod os_local; + #[doc(hidden)] + pub use os_local::{Key, thread_local_inner}; + } +} + +mod lazy { + use crate::cell::UnsafeCell; + use crate::hint; + use crate::mem; + + pub struct LazyKeyInner<T> { + inner: UnsafeCell<Option<T>>, + } + + impl<T> LazyKeyInner<T> { + pub const fn new() -> LazyKeyInner<T> { + LazyKeyInner { inner: UnsafeCell::new(None) } + } + + pub unsafe fn get(&self) -> Option<&'static T> { + // SAFETY: The caller must ensure no reference is ever handed out to + // the inner cell nor mutable reference to the Option<T> inside said + // cell. This make it safe to hand a reference, though the lifetime + // of 'static is itself unsafe, making the get method unsafe. + unsafe { (*self.inner.get()).as_ref() } + } + + /// The caller must ensure that no reference is active: this method + /// needs unique access. + pub unsafe fn initialize<F: FnOnce() -> T>(&self, init: F) -> &'static T { + // Execute the initialization up front, *then* move it into our slot, + // just in case initialization fails. + let value = init(); + let ptr = self.inner.get(); + + // SAFETY: + // + // note that this can in theory just be `*ptr = Some(value)`, but due to + // the compiler will currently codegen that pattern with something like: + // + // ptr::drop_in_place(ptr) + // ptr::write(ptr, Some(value)) + // + // Due to this pattern it's possible for the destructor of the value in + // `ptr` (e.g., if this is being recursively initialized) to re-access + // TLS, in which case there will be a `&` and `&mut` pointer to the same + // value (an aliasing violation). To avoid setting the "I'm running a + // destructor" flag we just use `mem::replace` which should sequence the + // operations a little differently and make this safe to call. + // + // The precondition also ensures that we are the only one accessing + // `self` at the moment so replacing is fine. + unsafe { + let _ = mem::replace(&mut *ptr, Some(value)); + } + + // SAFETY: With the call to `mem::replace` it is guaranteed there is + // a `Some` behind `ptr`, not a `None` so `unreachable_unchecked` + // will never be reached. + unsafe { + // After storing `Some` we want to get a reference to the contents of + // what we just stored. While we could use `unwrap` here and it should + // always work it empirically doesn't seem to always get optimized away, + // which means that using something like `try_with` can pull in + // panicking code and cause a large size bloat. + match *ptr { + Some(ref x) => x, + None => hint::unreachable_unchecked(), + } + } + } + + /// The other methods hand out references while taking &self. + /// As such, callers of this method must ensure no `&` and `&mut` are + /// available and used at the same time. + #[allow(unused)] + pub unsafe fn take(&mut self) -> Option<T> { + // SAFETY: See doc comment for this method. + unsafe { (*self.inner.get()).take() } + } + } +} + +/// Run a callback in a scenario which must not unwind (such as a `extern "C" +/// fn` declared in a user crate). If the callback unwinds anyway, then +/// `rtabort` with a message about thread local panicking on drop. +#[inline] +pub fn abort_on_dtor_unwind(f: impl FnOnce()) { + // Using a guard like this is lower cost. + let guard = DtorUnwindGuard; + f(); + core::mem::forget(guard); + + struct DtorUnwindGuard; + impl Drop for DtorUnwindGuard { + #[inline] + fn drop(&mut self) { + // This is not terribly descriptive, but it doesn't need to be as we'll + // already have printed a panic message at this point. + rtabort!("thread local panicked on drop"); + } + } +} |
