diff options
| author | Fenrir <[email protected]> | 2017-07-26 01:53:40 -0600 |
|---|---|---|
| committer | Fenrir <[email protected]> | 2017-07-26 01:53:40 -0600 |
| commit | 361c812e6aba4a414304457fb9170014159e4978 (patch) | |
| tree | 6ac98462bd1eb469b2b09623719142bc87d62861 /ctr-std/src/thread/local.rs | |
| parent | Merge pull request #36 from FenrirWolf/errDisp (diff) | |
| download | ctru-rs-361c812e6aba4a414304457fb9170014159e4978.tar.xz ctru-rs-361c812e6aba4a414304457fb9170014159e4978.zip | |
Thread fixes + module update
Diffstat (limited to 'ctr-std/src/thread/local.rs')
| -rw-r--r-- | ctr-std/src/thread/local.rs | 269 |
1 files changed, 197 insertions, 72 deletions
diff --git a/ctr-std/src/thread/local.rs b/ctr-std/src/thread/local.rs index 5166ddf..0172f89 100644 --- a/ctr-std/src/thread/local.rs +++ b/ctr-std/src/thread/local.rs @@ -19,17 +19,17 @@ use mem; /// A thread local storage key which owns its contents. /// /// This key uses the fastest possible implementation available to it for the -/// target platform. It is instantiated with the `thread_local!` macro and the -/// primary method is the `with` method. +/// target platform. It is instantiated with the [`thread_local!`] macro and the +/// primary method is the [`with`] method. /// -/// The `with` method yields a reference to the contained value which cannot be +/// The [`with`] method yields a reference to the contained value which cannot be /// sent across threads or escape the given closure. /// /// # Initialization and Destruction /// -/// Initialization is dynamically performed on the first call to `with()` -/// within a thread, and values support destructors which will be run when a -/// thread exits. +/// Initialization is dynamically performed on the first call to [`with`] +/// within a thread, and values that implement [`Drop`] get destructed when a +/// thread exits. Some caveats apply, which are explained below. /// /// # Examples /// @@ -74,9 +74,13 @@ use mem; /// destroyed, but not all platforms have this guard. Those platforms that do /// not guard typically have a synthetic limit after which point no more /// destructors are run. -/// 3. On OSX, initializing TLS during destruction of other TLS slots can +/// 3. On macOS, initializing TLS during destruction of other TLS slots can /// sometimes cancel *all* destructors for the current thread, whether or not /// the slots have already had their destructors run or not. +/// +/// [`with`]: ../../std/thread/struct.LocalKey.html#method.with +/// [`thread_local!`]: ../../std/macro.thread_local.html +/// [`Drop`]: ../../std/ops/trait.Drop.html #[stable(feature = "rust1", since = "1.0.0")] pub struct LocalKey<T: 'static> { // This outer `LocalKey<T>` type is what's going to be stored in statics, @@ -106,12 +110,12 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { } } -/// Declare a new thread local storage key of type `std::thread::LocalKey`. +/// Declare a new thread local storage key of type [`std::thread::LocalKey`]. /// /// # Syntax /// /// The macro wraps any number of static declarations and makes them thread local. -/// Each static may be public or private, and attributes are allowed. Example: +/// Publicity and attributes for each static are allowed. Example: /// /// ``` /// use std::cell::RefCell; @@ -124,37 +128,26 @@ impl<T: 'static> fmt::Debug for LocalKey<T> { /// # fn main() {} /// ``` /// -/// See [LocalKey documentation](thread/struct.LocalKey.html) for more +/// See [LocalKey documentation][`std::thread::LocalKey`] for more /// information. +/// +/// [`std::thread::LocalKey`]: ../std/thread/struct.LocalKey.html #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable] macro_rules! thread_local { - // rule 0: empty (base case for the recursion) + // empty (base case for the recursion) () => {}; - // rule 1: process multiple declarations where the first one is private - ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - thread_local!($(#[$attr])* static $name: $t = $init); // go to rule 2 - thread_local!($($rest)*); - ); - - // rule 2: handle a single private declaration - ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( - $(#[$attr])* static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init); - ); - - // rule 3: handle multiple declarations where the first one is public - ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( - thread_local!($(#[$attr])* pub static $name: $t = $init); // go to rule 4 + // process multiple declarations + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); thread_local!($($rest)*); ); - // rule 4: handle a single public declaration - ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr) => ( - $(#[$attr])* pub static $name: $crate::thread::LocalKey<$t> = - __thread_local_inner!($t, $init); + // handle a single declaration + ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => ( + __thread_local_inner!($(#[$attr])* $vis $name, $t, $init); ); } @@ -165,27 +158,29 @@ macro_rules! thread_local { #[macro_export] #[allow_internal_unstable] macro_rules! __thread_local_inner { - ($t:ty, $init:expr) => {{ - fn __init() -> $t { $init } - - fn __getit() -> $crate::option::Option< - &'static $crate::cell::UnsafeCell< - $crate::option::Option<$t>>> - { - #[thread_local] - #[cfg(target_thread_local)] - static __KEY: $crate::thread::__FastLocalKeyInner<$t> = - $crate::thread::__FastLocalKeyInner::new(); - - #[cfg(not(target_thread_local))] - static __KEY: $crate::thread::__OsLocalKeyInner<$t> = - $crate::thread::__OsLocalKeyInner::new(); - - __KEY.get() - } + ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $init:expr) => { + $(#[$attr])* $vis static $name: $crate::thread::LocalKey<$t> = { + fn __init() -> $t { $init } + + fn __getit() -> $crate::option::Option< + &'static $crate::cell::UnsafeCell< + $crate::option::Option<$t>>> + { + #[thread_local] + #[cfg(target_thread_local)] + static __KEY: $crate::thread::__FastLocalKeyInner<$t> = + $crate::thread::__FastLocalKeyInner::new(); + + #[cfg(not(target_thread_local))] + static __KEY: $crate::thread::__OsLocalKeyInner<$t> = + $crate::thread::__OsLocalKeyInner::new(); + + __KEY.get() + } - $crate::thread::LocalKey::new(__getit, __init) - }} + $crate::thread::LocalKey::new(__getit, __init) + }; + } } /// Indicator of the state of a thread local storage key. @@ -195,11 +190,13 @@ macro_rules! __thread_local_inner { #[derive(Debug, Eq, PartialEq, Copy, Clone)] pub enum LocalKeyState { /// All keys are in this state whenever a thread starts. Keys will - /// transition to the `Valid` state once the first call to `with` happens + /// transition to the `Valid` state once the first call to [`with`] happens /// and the initialization expression succeeds. /// /// Keys in the `Uninitialized` state will yield a reference to the closure - /// passed to `with` so long as the initialization routine does not panic. + /// passed to [`with`] so long as the initialization routine does not panic. + /// + /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with Uninitialized, /// Once a key has been accessed successfully, it will enter the `Valid` @@ -208,7 +205,9 @@ pub enum LocalKeyState { /// `Destroyed` state. /// /// Keys in the `Valid` state will be guaranteed to yield a reference to the - /// closure passed to `with`. + /// closure passed to [`with`]. + /// + /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with Valid, /// When a thread exits, the destructors for keys will be run (if @@ -216,10 +215,38 @@ pub enum LocalKeyState { /// destructor has run, a key is in the `Destroyed` state. /// /// Keys in the `Destroyed` states will trigger a panic when accessed via - /// `with`. + /// [`with`]. + /// + /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with Destroyed, } +/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with). +#[unstable(feature = "thread_local_state", + reason = "state querying was recently added", + issue = "27716")] +pub struct AccessError { + _private: (), +} + +#[unstable(feature = "thread_local_state", + reason = "state querying was recently added", + issue = "27716")] +impl fmt::Debug for AccessError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("AccessError").finish() + } +} + +#[unstable(feature = "thread_local_state", + reason = "state querying was recently added", + issue = "27716")] +impl fmt::Display for AccessError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt("already destroyed", f) + } +} + impl<T: 'static> LocalKey<T> { #[doc(hidden)] #[unstable(feature = "thread_local_internals", @@ -246,15 +273,8 @@ impl<T: 'static> LocalKey<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn with<F, R>(&'static self, f: F) -> R where F: FnOnce(&T) -> R { - unsafe { - let slot = (self.inner)(); - let slot = slot.expect("cannot access a TLS value during or \ - after it is destroyed"); - f(match *slot.get() { - Some(ref inner) => inner, - None => self.init(slot), - }) - } + self.try_with(f).expect("cannot access a TLS value during or \ + after it is destroyed") } unsafe fn init(&self, slot: &UnsafeCell<Option<T>>) -> &T { @@ -283,23 +303,26 @@ impl<T: 'static> LocalKey<T> { /// Query the current state of this key. /// /// A key is initially in the `Uninitialized` state whenever a thread - /// starts. It will remain in this state up until the first call to `with` + /// starts. It will remain in this state up until the first call to [`with`] /// within a thread has run the initialization expression successfully. /// /// Once the initialization expression succeeds, the key transitions to the - /// `Valid` state which will guarantee that future calls to `with` will + /// `Valid` state which will guarantee that future calls to [`with`] will /// succeed within the thread. /// /// When a thread exits, each key will be destroyed in turn, and as keys are /// destroyed they will enter the `Destroyed` state just before the /// destructor starts to run. Keys may remain in the `Destroyed` state after /// destruction has completed. Keys without destructors (e.g. with types - /// that are `Copy`), may never enter the `Destroyed` state. + /// that are [`Copy`]), may never enter the `Destroyed` state. /// /// Keys in the `Uninitialized` state can be accessed so long as the /// initialization does not panic. Keys in the `Valid` state are guaranteed /// to be able to be accessed. Keys in the `Destroyed` state will panic on - /// any call to `with`. + /// any call to [`with`]. + /// + /// [`with`]: ../../std/thread/struct.LocalKey.html#method.with + /// [`Copy`]: ../../std/marker/trait.Copy.html #[unstable(feature = "thread_local_state", reason = "state querying was recently added", issue = "27716")] @@ -316,6 +339,108 @@ impl<T: 'static> LocalKey<T> { } } } + + /// Acquires a reference to the value in this TLS key. + /// + /// This will lazily initialize the value if this thread has not referenced + /// this key yet. If the key has been destroyed (which may happen if this is called + /// in a destructor), this function will return a ThreadLocalError. + /// + /// # Panics + /// + /// This function will still `panic!()` if the key is uninitialized and the + /// key's initializer panics. + #[unstable(feature = "thread_local_state", + reason = "state querying was recently added", + issue = "27716")] + pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError> + where F: FnOnce(&T) -> R { + unsafe { + let slot = (self.inner)().ok_or(AccessError { + _private: (), + })?; + Ok(f(match *slot.get() { + Some(ref inner) => inner, + None => self.init(slot), + })) + } + } +} + +#[doc(hidden)] +#[cfg(target_thread_local)] +pub mod fast { + use cell::{Cell, UnsafeCell}; + use fmt; + use mem; + use ptr; + use sys::fast_thread_local::{register_dtor, requires_move_before_drop}; + + pub struct Key<T> { + inner: UnsafeCell<Option<T>>, + + // Metadata to keep track of the state of the destructor. Remember that + // these variables are thread-local, not global. + dtor_registered: Cell<bool>, + dtor_running: Cell<bool>, + } + + impl<T> fmt::Debug for Key<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("Key { .. }") + } + } + + unsafe impl<T> ::marker::Sync for Key<T> { } + + impl<T> Key<T> { + pub const fn new() -> Key<T> { + Key { + inner: UnsafeCell::new(None), + dtor_registered: Cell::new(false), + dtor_running: Cell::new(false) + } + } + + pub fn get(&'static self) -> Option<&'static UnsafeCell<Option<T>>> { + unsafe { + if mem::needs_drop::<T>() && self.dtor_running.get() { + return None + } + self.register_dtor(); + } + Some(&self.inner) + } + + unsafe fn register_dtor(&self) { + if !mem::needs_drop::<T>() || self.dtor_registered.get() { + return + } + + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + self.dtor_registered.set(true); + } + } + + unsafe extern fn destroy_value<T>(ptr: *mut u8) { + let ptr = ptr as *mut Key<T>; + // Right before we run the user destructor be sure to flag the + // destructor as running for this thread so calls to `get` will return + // `None`. + (*ptr).dtor_running.set(true); + + // Some implementations may require us to move the value before we drop + // it as it could get re-initialized in-place during destruction. + // + // Hence, we use `ptr::read` on those platforms (to move to a "safe" + // location) instead of drop_in_place. + if requires_move_before_drop() { + ptr::read((*ptr).inner.get()); + } else { + ptr::drop_in_place((*ptr).inner.get()); + } + } } #[doc(hidden)] @@ -363,8 +488,8 @@ pub mod os { return Some(&(*ptr).value); } - // If the lookup returned null, we haven't initialized our own local - // copy, so do that now. + // If the lookup returned null, we haven't initialized our own + // local copy, so do that now. let ptr: Box<Value<T>> = box Value { key: self, value: UnsafeCell::new(None), @@ -376,7 +501,7 @@ pub mod os { } } - pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { + unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { // The OS TLS ensures that this key contains a NULL value when this // destructor starts to run. We set it back to a sentinel value of 1 to // ensure that any future calls to `get` for this thread will return @@ -524,9 +649,9 @@ mod tests { } // Note that this test will deadlock if TLS destructors aren't run (this - // requires the destructor to be run to pass the test). OSX has a known bug + // requires the destructor to be run to pass the test). macOS has a known bug // where dtors-in-dtors may cancel other destructors, so we just ignore this - // test on OSX. + // test on macOS. #[test] #[cfg_attr(target_os = "macos", ignore)] fn dtors_in_dtors_in_dtors() { |