diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-11-14 14:20:57 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-11-23 23:37:16 -0800 |
| commit | a9c1152c4bf72132806cb76045b3464d59db07da (patch) | |
| tree | 89ba92d5f5788e3323b75ca003bf74661a94e4de /src/libstd | |
| parent | 4e5259503cd8aac9905c7ac6d68d0c4caab1d28c (diff) | |
| download | rust-a9c1152c4bf72132806cb76045b3464d59db07da.tar.gz rust-a9c1152c4bf72132806cb76045b3464d59db07da.zip | |
std: Add a new top-level thread_local module
This commit removes the `std::local_data` module in favor of a new
`std::thread_local` module providing thread local storage. The module provides
two variants of TLS: one which owns its contents and one which is based on
scoped references. Each implementation has pros and cons listed in the
documentation.
Both flavors have accessors through a function called `with` which yield a
reference to a closure provided. Both flavors also panic if a reference cannot
be yielded and provide a function to test whether an access would panic or not.
This is an implementation of [RFC 461][rfc] and full details can be found in
that RFC.
This is a breaking change due to the removal of the `std::local_data` module.
All users can migrate to the new thread local system like so:
thread_local!(static FOO: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None)))
The old `local_data` module inherently contained the `Rc<RefCell<Option<T>>>` as
an implementation detail which must now be explicitly stated by users.
[rfc]: https://github.com/rust-lang/rfcs/pull/461
[breaking-change]
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/collections/hash/map.rs | 133 | ||||
| -rw-r--r-- | src/libstd/failure.rs | 15 | ||||
| -rw-r--r-- | src/libstd/io/stdio.rs | 31 | ||||
| -rw-r--r-- | src/libstd/lib.rs | 31 | ||||
| -rw-r--r-- | src/libstd/macros.rs | 22 | ||||
| -rw-r--r-- | src/libstd/rand/mod.rs | 31 | ||||
| -rw-r--r-- | src/libstd/sync/future.rs | 24 | ||||
| -rw-r--r-- | src/libstd/sys/common/mod.rs | 1 | ||||
| -rw-r--r-- | src/libstd/sys/common/thread_local.rs | 306 | ||||
| -rw-r--r-- | src/libstd/sys/unix/mod.rs | 7 | ||||
| -rw-r--r-- | src/libstd/sys/unix/thread_local.rs | 52 | ||||
| -rw-r--r-- | src/libstd/sys/windows/mod.rs | 7 | ||||
| -rw-r--r-- | src/libstd/sys/windows/thread_local.rs | 238 | ||||
| -rw-r--r-- | src/libstd/thread_local/mod.rs | 634 | ||||
| -rw-r--r-- | src/libstd/thread_local/scoped.rs | 261 |
15 files changed, 1634 insertions, 159 deletions
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 69375e8d4f8..662ae913764 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1471,7 +1471,7 @@ mod test_map { assert_eq!(*m.get(&2).unwrap(), 4); } - local_data_key!(drop_vector: RefCell<Vec<int>>) + thread_local!(static DROP_VECTOR: RefCell<Vec<int>> = RefCell::new(Vec::new())) #[deriving(Hash, PartialEq, Eq)] struct Dropable { @@ -1480,8 +1480,9 @@ mod test_map { impl Dropable { fn new(k: uint) -> Dropable { - let v = drop_vector.get().unwrap(); - v.borrow_mut().as_mut_slice()[k] += 1; + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[k] += 1; + }); Dropable { k: k } } @@ -1489,8 +1490,9 @@ mod test_map { impl Drop for Dropable { fn drop(&mut self) { - let v = drop_vector.get().unwrap(); - v.borrow_mut().as_mut_slice()[self.k] -= 1; + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[self.k] -= 1; + }); } } @@ -1502,16 +1504,18 @@ mod test_map { #[test] fn test_drops() { - drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i)))); + DROP_VECTOR.with(|slot| { + *slot.borrow_mut() = Vec::from_elem(200, 0i); + }); { let mut m = HashMap::new(); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 0); + } + }); for i in range(0u, 100) { let d1 = Dropable::new(i); @@ -1519,11 +1523,11 @@ mod test_map { m.insert(d1, d2); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 1); + } + }); for i in range(0u, 50) { let k = Dropable::new(i); @@ -1531,41 +1535,46 @@ mod test_map { assert!(v.is_some()); - let v = drop_vector.get().unwrap(); - assert_eq!(v.borrow().as_slice()[i], 1); - assert_eq!(v.borrow().as_slice()[i+100], 1); + DROP_VECTOR.with(|v| { + assert_eq!(v.borrow().as_slice()[i], 1); + assert_eq!(v.borrow().as_slice()[i+100], 1); + }); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 50) { - assert_eq!(v.borrow().as_slice()[i], 0); - assert_eq!(v.borrow().as_slice()[i+100], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 50) { + assert_eq!(v.borrow().as_slice()[i], 0); + assert_eq!(v.borrow().as_slice()[i+100], 0); + } - for i in range(50u, 100) { - assert_eq!(v.borrow().as_slice()[i], 1); - assert_eq!(v.borrow().as_slice()[i+100], 1); - } + for i in range(50u, 100) { + assert_eq!(v.borrow().as_slice()[i], 1); + assert_eq!(v.borrow().as_slice()[i+100], 1); + } + }); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 0); + } + }); } #[test] fn test_move_iter_drops() { - drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i)))); + DROP_VECTOR.with(|v| { + *v.borrow_mut() = Vec::from_elem(200, 0i); + }); let hm = { let mut hm = HashMap::new(); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 0); + } + }); for i in range(0u, 100) { let d1 = Dropable::new(i); @@ -1573,11 +1582,11 @@ mod test_map { hm.insert(d1, d2); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 1); + } + }); hm }; @@ -1588,31 +1597,33 @@ mod test_map { { let mut half = hm.into_iter().take(50); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 1); + } + }); for _ in half {} - let v = drop_vector.get().unwrap(); - let nk = range(0u, 100).filter(|&i| { - v.borrow().as_slice()[i] == 1 - }).count(); + DROP_VECTOR.with(|v| { + let nk = range(0u, 100).filter(|&i| { + v.borrow().as_slice()[i] == 1 + }).count(); - let nv = range(0u, 100).filter(|&i| { - v.borrow().as_slice()[i+100] == 1 - }).count(); + let nv = range(0u, 100).filter(|&i| { + v.borrow().as_slice()[i+100] == 1 + }).count(); - assert_eq!(nk, 50); - assert_eq!(nv, 50); + assert_eq!(nk, 50); + assert_eq!(nv, 50); + }); }; - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow().as_slice()[i], 0); + } + }); } #[test] diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index c23e043c174..32a8be22902 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -12,10 +12,11 @@ use alloc::boxed::Box; use any::{Any, AnyRefExt}; +use cell::RefCell; use fmt; use io::{Writer, IoResult}; use kinds::Send; -use option::{Some, None}; +use option::{Some, None, Option}; use result::Ok; use rt::backtrace; use rustrt::{Stderr, Stdio}; @@ -25,7 +26,9 @@ use str::Str; use string::String; // Defined in this module instead of io::stdio so that the unwinding -local_data_key!(pub local_stderr: Box<Writer + Send>) +thread_local!(pub static LOCAL_STDERR: RefCell<Option<Box<Writer + Send>>> = { + RefCell::new(None) +}) impl Writer for Stdio { fn write(&mut self, bytes: &[u8]) -> IoResult<()> { @@ -74,7 +77,8 @@ pub fn on_fail(obj: &Any + Send, file: &'static str, line: uint) { { let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); - match local_stderr.replace(None) { + let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); + match prev { Some(mut stderr) => { // FIXME: what to do when the task printing panics? let _ = writeln!(stderr, @@ -83,7 +87,10 @@ pub fn on_fail(obj: &Any + Send, file: &'static str, line: uint) { if backtrace::log_enabled() { let _ = backtrace::write(&mut *stderr); } - local_stderr.replace(Some(stderr)); + let mut s = Some(stderr); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); } None => { let _ = writeln!(&mut err, "task '{}' panicked at '{}', {}:{}", diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 7374668a69d..d450e9f1dce 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -29,22 +29,24 @@ out.write(b"Hello, world!"); use self::StdSource::*; -use failure::local_stderr; +use boxed::Box; +use cell::RefCell; +use failure::LOCAL_STDERR; use fmt; use io::{Reader, Writer, IoResult, IoError, OtherIoError, standard_error, EndOfFile, LineBufferedWriter, BufferedReader}; use iter::Iterator; use kinds::Send; use libc; +use mem; use option::{Option, Some, None}; -use boxed::Box; -use sys::{fs, tty}; use result::{Ok, Err}; use rustrt; use rustrt::local::Local; use rustrt::task::Task; use slice::SlicePrelude; use str::StrPrelude; +use sys::{fs, tty}; use uint; // And so begins the tale of acquiring a uv handle to a stdio stream on all @@ -87,7 +89,9 @@ fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T { } } -local_data_key!(local_stdout: Box<Writer + Send>) +thread_local!(static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = { + RefCell::new(None) +}) /// Creates a new non-blocking handle to the stdin of the current process. /// @@ -167,7 +171,10 @@ pub fn stderr_raw() -> StdWriter { /// Note that this does not need to be called for all new tasks; the default /// output handle is to the process's stdout stream. pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> { - local_stdout.replace(Some(stdout)).and_then(|mut s| { + let mut new = Some(stdout); + LOCAL_STDOUT.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { let _ = s.flush(); Some(s) }) @@ -182,7 +189,10 @@ pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> { /// Note that this does not need to be called for all new tasks; the default /// output handle is to the process's stderr stream. pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> { - local_stderr.replace(Some(stderr)).and_then(|mut s| { + let mut new = Some(stderr); + LOCAL_STDERR.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { let _ = s.flush(); Some(s) }) @@ -200,11 +210,16 @@ pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> { // }) fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) { let result = if Local::exists(None::<Task>) { - let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| { + let mut my_stdout = LOCAL_STDOUT.with(|slot| { + slot.borrow_mut().take() + }).unwrap_or_else(|| { box stdout() as Box<Writer + Send> }); let result = f(&mut *my_stdout); - local_stdout.replace(Some(my_stdout)); + let mut var = Some(my_stdout); + LOCAL_STDOUT.with(|slot| { + *slot.borrow_mut() = var.take(); + }); result } else { let mut io = rustrt::Stdout; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b35c49efdd8..77da727e94d 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -170,7 +170,6 @@ pub use core_collections::string; pub use core_collections::vec; pub use rustrt::c_str; -pub use rustrt::local_data; pub use unicode::char; @@ -209,17 +208,25 @@ pub mod prelude; #[path = "num/f32.rs"] pub mod f32; #[path = "num/f64.rs"] pub mod f64; -pub mod rand; - pub mod ascii; -pub mod time; - /* Common traits */ pub mod error; pub mod num; +/* Runtime and platform support */ + +pub mod thread_local; +pub mod c_vec; +pub mod dynamic_lib; +pub mod fmt; +pub mod io; +pub mod os; +pub mod path; +pub mod rand; +pub mod time; + /* Common data structures */ pub mod collections; @@ -230,15 +237,6 @@ pub mod hash; pub mod task; pub mod sync; -/* Runtime and platform support */ - -pub mod c_vec; -pub mod dynamic_lib; -pub mod os; -pub mod io; -pub mod path; -pub mod fmt; - #[cfg(unix)] #[path = "sys/unix/mod.rs"] mod sys; #[cfg(windows)] @@ -263,10 +261,12 @@ mod std { pub use error; // used for try!() pub use fmt; // used for any formatting strings pub use io; // used for println!() - pub use local_data; // used for local_data_key!() pub use option; // used for bitflags!{} pub use rt; // used for panic!() pub use vec; // used for vec![] + pub use cell; // used for tls! + pub use thread_local; // used for thread_local! + pub use kinds; // used for tls! // The test runner calls ::std::os::args() but really wants realstd #[cfg(test)] pub use realstd::os as os; @@ -276,4 +276,5 @@ mod std { pub use slice; pub use boxed; // used for vec![] + } diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 18c109d92a0..c3260225d0a 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -304,28 +304,6 @@ macro_rules! println( ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*)) ) -/// Declare a task-local key with a specific type. -/// -/// # Example -/// -/// ``` -/// local_data_key!(my_integer: int) -/// -/// my_integer.replace(Some(2)); -/// println!("{}", my_integer.get().map(|a| *a)); -/// ``` -#[macro_export] -macro_rules! local_data_key( - ($name:ident: $ty:ty) => ( - #[allow(non_upper_case_globals)] - static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey; - ); - (pub $name:ident: $ty:ty) => ( - #[allow(non_upper_case_globals)] - pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey; - ); -) - /// Helper macro for unwrapping `Result` values while returning early with an /// error if the value of the expression is `Err`. For more information, see /// `std::io`. diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 08eb7350bcf..f9f9147b107 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -226,7 +226,6 @@ use clone::Clone; use io::IoResult; use iter::Iterator; use mem; -use option::{Some, None}; use rc::Rc; use result::{Ok, Err}; use vec::Vec; @@ -337,24 +336,18 @@ pub struct TaskRng { /// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. pub fn task_rng() -> TaskRng { // used to make space in TLS for a random number generator - local_data_key!(TASK_RNG_KEY: Rc<RefCell<TaskRngInner>>) - - match TASK_RNG_KEY.get() { - None => { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize task_rng: {}", e) - }; - let rng = reseeding::ReseedingRng::new(r, - TASK_RNG_RESEED_THRESHOLD, - TaskRngReseeder); - let rng = Rc::new(RefCell::new(rng)); - TASK_RNG_KEY.replace(Some(rng.clone())); - - TaskRng { rng: rng } - } - Some(rng) => TaskRng { rng: rng.clone() } - } + thread_local!(static TASK_RNG_KEY: Rc<RefCell<TaskRngInner>> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize task_rng: {}", e) + }; + let rng = reseeding::ReseedingRng::new(r, + TASK_RNG_RESEED_THRESHOLD, + TaskRngReseeder); + Rc::new(RefCell::new(rng)) + }) + + TaskRng { rng: TASK_RNG_KEY.with(|t| t.clone()) } } impl Rng for TaskRng { diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index e37d1f83877..3e1ba8cebf8 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -210,28 +210,4 @@ mod test { }); assert_eq!(rx.recv(), expected); } - - #[test] - fn test_dropped_future_doesnt_panic() { - struct Bomb(Sender<bool>); - - local_data_key!(LOCAL: Bomb) - - impl Drop for Bomb { - fn drop(&mut self) { - let Bomb(ref tx) = *self; - tx.send(task::failing()); - } - } - - // Spawn a future, but drop it immediately. When we receive the result - // later on, we should never view the task as having panicked. - let (tx, rx) = channel(); - drop(Future::spawn(proc() { - LOCAL.replace(Some(Bomb(tx))); - })); - - // Make sure the future didn't panic the task. - assert!(!rx.recv()); - } } diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index cacb128faa5..77edd7c5c4e 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -21,6 +21,7 @@ use collections; pub mod net; pub mod helper_thread; +pub mod thread_local; // common error constructors diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs new file mode 100644 index 00000000000..9ad38cbaf65 --- /dev/null +++ b/src/libstd/sys/common/thread_local.rs @@ -0,0 +1,306 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! OS-based thread local storage +//! +//! This module provides an implementation of OS-based thread local storage, +//! using the native OS-provided facilities (think `TlsAlloc` or +//! `pthread_setspecific`). The interface of this differs from the other types +//! of thread-local-storage provided in this crate in that OS-based TLS can only +//! get/set pointers, +//! +//! This module also provides two flavors of TLS. One is intended for static +//! initialization, and does not contain a `Drop` implementation to deallocate +//! the OS-TLS key. The other is a type which does implement `Drop` and hence +//! has a safe interface. +//! +//! # Usage +//! +//! This module should likely not be used directly unless other primitives are +//! being built on. types such as `thread_local::scoped::Key` are likely much +//! more useful in practice than this OS-based version which likely requires +//! unsafe code to interoperate with. +//! +//! # Example +//! +//! Using a dynamically allocated TLS key. Note that this key can be shared +//! among many threads via an `Arc`. +//! +//! ```rust,ignore +//! let key = Key::new(None); +//! assert!(key.get().is_null()); +//! key.set(1 as *mut u8); +//! assert!(!key.get().is_null()); +//! +//! drop(key); // deallocate this TLS slot. +//! ``` +//! +//! Sometimes a statically allocated key is either required or easier to work +//! with, however. +//! +//! ```rust,ignore +//! static KEY: StaticKey = INIT; +//! +//! unsafe { +//! assert!(KEY.get().is_null()); +//! KEY.set(1 as *mut u8); +//! } +//! ``` + +#![allow(non_camel_case_types)] + +use prelude::*; + +use kinds::marker; +use mem; +use rustrt::exclusive::Exclusive; +use rustrt; +use sync::atomic::{mod, AtomicUint}; +use sync::{Once, ONCE_INIT}; + +use sys::thread_local as imp; + +/// A type for TLS keys that are statically allocated. +/// +/// This type is entirely `unsafe` to use as it does not protect against +/// use-after-deallocation or use-during-deallocation. +/// +/// The actual OS-TLS key is lazily allocated when this is used for the first +/// time. The key is also deallocated when the Rust runtime exits or `destroy` +/// is called, whichever comes first. +/// +/// # Example +/// +/// ```ignore +/// use tls::os::{StaticKey, INIT}; +/// +/// static KEY: StaticKey = INIT; +/// +/// unsafe { +/// assert!(KEY.get().is_null()); +/// KEY.set(1 as *mut u8); +/// } +/// ``` +pub struct StaticKey { + /// Inner static TLS key (internals), created with by `INIT_INNER` in this + /// module. + pub inner: StaticKeyInner, + /// Destructor for the TLS value. + /// + /// See `Key::new` for information about when the destructor runs and how + /// it runs. + pub dtor: Option<unsafe extern fn(*mut u8)>, +} + +/// Inner contents of `StaticKey`, created by the `INIT_INNER` constant. +pub struct StaticKeyInner { + key: AtomicUint, + nc: marker::NoCopy, +} + +/// A type for a safely managed OS-based TLS slot. +/// +/// This type allocates an OS TLS key when it is initialized and will deallocate +/// the key when it falls out of scope. When compared with `StaticKey`, this +/// type is entirely safe to use. +/// +/// Implementations will likely, however, contain unsafe code as this type only +/// operates on `*mut u8`, an unsafe pointer. +/// +/// # Example +/// +/// ```rust,ignore +/// use tls::os::Key; +/// +/// let key = Key::new(None); +/// assert!(key.get().is_null()); +/// key.set(1 as *mut u8); +/// assert!(!key.get().is_null()); +/// +/// drop(key); // deallocate this TLS slot. +/// ``` +pub struct Key { + key: imp::Key, +} + +/// Constant initialization value for static TLS keys. +/// +/// This value specifies no destructor by default. +pub const INIT: StaticKey = StaticKey { + inner: INIT_INNER, + dtor: None, +}; + +/// Constant initialization value for the inner part of static TLS keys. +/// +/// This value allows specific configuration of the destructor for a TLS key. +pub const INIT_INNER: StaticKeyInner = StaticKeyInner { + key: atomic::INIT_ATOMIC_UINT, + nc: marker::NoCopy, +}; + +static INIT_KEYS: Once = ONCE_INIT; +static mut KEYS: *mut Exclusive<Vec<imp::Key>> = 0 as *mut _; + +impl StaticKey { + /// Gets the value associated with this TLS key + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) } + + /// Sets this TLS key to a new value. + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } + + /// Deallocates this OS TLS key. + /// + /// This function is unsafe as there is no guarantee that the key is not + /// currently in use by other threads or will not ever be used again. + /// + /// Note that this does *not* run the user-provided destructor if one was + /// specified at definition time. Doing so must be done manually. + pub unsafe fn destroy(&self) { + match self.inner.key.swap(0, atomic::SeqCst) { + 0 => {} + n => { unregister_key(n as imp::Key); imp::destroy(n as imp::Key) } + } + } + + #[inline] + unsafe fn key(&self) -> imp::Key { + match self.inner.key.load(atomic::Relaxed) { + 0 => self.lazy_init() as imp::Key, + n => n as imp::Key + } + } + + unsafe fn lazy_init(&self) -> uint { + let key = imp::create(self.dtor); + assert!(key != 0); + match self.inner.key.compare_and_swap(0, key as uint, atomic::SeqCst) { + // The CAS succeeded, so we've created the actual key + 0 => { + register_key(key); + key as uint + } + // If someone beat us to the punch, use their key instead + n => { imp::destroy(key); n } + } + } +} + +impl Key { + /// Create a new managed OS TLS key. + /// + /// This key will be deallocated when the key falls out of scope. + /// + /// The argument provided is an optionally-specified destructor for the + /// value of this TLS key. When a thread exits and the value for this key + /// is non-null the destructor will be invoked. The TLS value will be reset + /// to null before the destructor is invoked. + /// + /// Note that the destructor will not be run when the `Key` goes out of + /// scope. + #[inline] + pub fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + Key { key: unsafe { imp::create(dtor) } } + } + + /// See StaticKey::get + #[inline] + pub fn get(&self) -> *mut u8 { + unsafe { imp::get(self.key) } + } + + /// See StaticKey::set + #[inline] + pub fn set(&self, val: *mut u8) { + unsafe { imp::set(self.key, val) } + } +} + +impl Drop for Key { + fn drop(&mut self) { + unsafe { imp::destroy(self.key) } + } +} + +fn init_keys() { + let keys = box Exclusive::new(Vec::<imp::Key>::new()); + unsafe { + KEYS = mem::transmute(keys); + } + + rustrt::at_exit(proc() unsafe { + let keys: Box<Exclusive<Vec<imp::Key>>> = mem::transmute(KEYS); + KEYS = 0 as *mut _; + let keys = keys.lock(); + for key in keys.iter() { + imp::destroy(*key); + } + }); +} + +fn register_key(key: imp::Key) { + INIT_KEYS.doit(init_keys); + let mut keys = unsafe { (*KEYS).lock() }; + keys.push(key); +} + +fn unregister_key(key: imp::Key) { + INIT_KEYS.doit(init_keys); + let mut keys = unsafe { (*KEYS).lock() }; + keys.retain(|k| *k != key); +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::{Key, StaticKey, INIT_INNER}; + + fn assert_sync<T: Sync>() {} + fn assert_send<T: Send>() {} + + #[test] + fn smoke() { + assert_sync::<Key>(); + assert_send::<Key>(); + + let k1 = Key::new(None); + let k2 = Key::new(None); + assert!(k1.get().is_null()); + assert!(k2.get().is_null()); + k1.set(1 as *mut _); + k2.set(2 as *mut _); + assert_eq!(k1.get() as uint, 1); + assert_eq!(k2.get() as uint, 2); + } + + #[test] + fn statik() { + static K1: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + static K2: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + + unsafe { + assert!(K1.get().is_null()); + assert!(K2.get().is_null()); + K1.set(1 as *mut _); + K2.set(2 as *mut _); + assert_eq!(K1.get() as uint, 1); + assert_eq!(K2.get() as uint, 2); + } + } +} + diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 664a6a1e70c..7a5317b578d 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -34,14 +34,15 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( pub mod c; pub mod fs; +pub mod helper_signal; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod tcp; pub mod timer; +pub mod thread_local; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs new file mode 100644 index 00000000000..b300e93eeb6 --- /dev/null +++ b/src/libstd/sys/unix/thread_local.rs @@ -0,0 +1,52 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; +use libc::c_int; + +pub type Key = pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + let mut key = 0; + assert_eq!(pthread_key_create(&mut key, dtor), 0); + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = pthread_setspecific(key, value); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + pthread_getspecific(key) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[cfg(target_os = "macos")] +type pthread_key_t = ::libc::c_ulong; + +#[cfg(not(target_os = "macos"))] +type pthread_key_t = ::libc::c_uint; + +extern { + fn pthread_key_create(key: *mut pthread_key_t, + dtor: Option<unsafe extern fn(*mut u8)>) -> c_int; + fn pthread_key_delete(key: pthread_key_t) -> c_int; + fn pthread_getspecific(key: pthread_key_t) -> *mut u8; + fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int; +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 815ace21f87..a785ccfe804 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -35,14 +35,15 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( pub mod c; pub mod fs; +pub mod helper_signal; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod tcp; +pub mod thread_local; pub mod timer; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs new file mode 100644 index 00000000000..b841f6d3a2b --- /dev/null +++ b/src/libstd/sys/windows/thread_local.rs @@ -0,0 +1,238 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::*; + +use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; + +use mem; +use rustrt; +use rustrt::exclusive::Exclusive; +use sync::{ONCE_INIT, Once}; + +pub type Key = DWORD; +pub type Dtor = unsafe extern fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of tasks, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a TLS key is destroyed, we're sure to remove it from the dtor list +// if it's in there. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base +// /threading/thread_local_storage_win.cc#L42 + +static INIT_DTORS: Once = ONCE_INIT; +static mut DTORS: *mut Exclusive<Vec<(Key, Dtor)>> = 0 as *mut _; + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; + let key = TlsAlloc(); + assert!(key != TLS_OUT_OF_INDEXES); + match dtor { + Some(f) => register_dtor(key, f), + None => {} + } + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = TlsSetValue(key, value as LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + if unregister_dtor(key) { + // FIXME: Currently if a key has a destructor associated with it we + // can't actually ever unregister it. If we were to + // unregister it, then any key destruction would have to be + // serialized with respect to actually running destructors. + // + // We want to avoid a race where right before run_dtors runs + // some destructors TlsFree is called. Allowing the call to + // TlsFree would imply that the caller understands that *all + // known threads* are not exiting, which is quite a difficult + // thing to know! + // + // For now we just leak all keys with dtors to "fix" this. + // Note that source [2] above shows precedent for this sort + // of strategy. + } else { + let r = TlsFree(key); + debug_assert!(r != 0); + } +} + +extern "system" { + fn TlsAlloc() -> DWORD; + fn TlsFree(dwTlsIndex: DWORD) -> BOOL; + fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; + fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// These functions are associated with registering and unregistering +// destructors. They're pretty simple, they just push onto a vector and scan +// a vector currently. +// +// FIXME: This could probably be at least a little faster with a BTree. + +fn init_dtors() { + let dtors = box Exclusive::new(Vec::<(Key, Dtor)>::new()); + unsafe { + DTORS = mem::transmute(dtors); + } + + rustrt::at_exit(proc() unsafe { + mem::transmute::<_, Box<Exclusive<Vec<(Key, Dtor)>>>>(DTORS); + DTORS = 0 as *mut _; + }); +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + INIT_DTORS.doit(init_dtors); + let mut dtors = (*DTORS).lock(); + dtors.push((key, dtor)); +} + +unsafe fn unregister_dtor(key: Key) -> bool { + if DTORS.is_null() { return false } + let mut dtors = (*DTORS).lock(); + let before = dtors.len(); + dtors.retain(|&(k, _)| k != key); + dtors.len() != before +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not qute sure!) There are a few events that +// this gets invoked for, but we're currentl only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. To do this we clone a +// local copy of the dtor list to start out with. This is our fudgy attempt +// to not hold the lock while destructors run and not worry about the list +// changing while we're looking at it. +// +// Once we've got a list of what to run, we iterate over all keys, check +// their values, and then run destructors if the values turn out to be non +// null (setting them to null just beforehand). We do this a few times in a +// loop to basically match Unix semantics. If we don't reach a fixed point +// after a short while then we just inevitably leak something most likely. +// +// # The article mentions crazy stuff about "/INCLUDE"? +// +// It sure does! This seems to work for now, so maybe we'll just run into +// that if we start linking with msvc? + +#[link_section = ".CRT$XLB"] +#[linkage = "external"] +#[allow(warnings)] +pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD, + LPVOID) = + on_tls_callback; + +#[allow(warnings)] +unsafe extern "system" fn on_tls_callback(h: LPVOID, + dwReason: DWORD, + pv: LPVOID) { + const DLL_THREAD_DETACH: DWORD = 3; + const DLL_PROCESS_DETACH: DWORD = 0; + if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH { + run_dtors(); + } +} + +unsafe fn run_dtors() { + if DTORS.is_null() { return } + let mut any_run = true; + for _ in range(0, 5i) { + if !any_run { break } + any_run = false; + let dtors = (*DTORS).lock().iter().map(|p| *p).collect::<Vec<_>>(); + for &(key, dtor) in dtors.iter() { + let ptr = TlsGetValue(key); + if !ptr.is_null() { + TlsSetValue(key, 0 as *mut _); + dtor(ptr as *mut _); + any_run = true; + } + } + } +} diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs new file mode 100644 index 00000000000..e2e8461ea54 --- /dev/null +++ b/src/libstd/thread_local/mod.rs @@ -0,0 +1,634 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Thread local storage +//! +//! This module provides an implementation of thread local storage for Rust +//! programs. Thread local storage is a method of storing data into a global +//! variable which each thread in the program will have its own copy of. +//! Threads do not share this data, so accesses do not need to be synchronized. +//! +//! At a high level, this module provides two variants of storage: +//! +//! * Owning thread local storage. This is a type of thread local key which +//! owns the value that it contains, and will destroy the value when the +//! thread exits. This variant is created with the `thread_local!` macro and +//! can contain any value which is `'static` (no borrowed pointers. +//! +//! * Scoped thread local storage. This type of key is used to store a reference +//! to a value into local storage temporarily for the scope of a function +//! call. There are no restrictions on what types of values can be placed +//! into this key. +//! +//! Both forms of thread local storage provide an accessor function, `with`, +//! which will yield a shared reference to the value to the specified +//! closure. Thread local keys only allow shared access to values as there is no +//! way to guarantee uniqueness if a mutable borrow was allowed. Most values +//! will want to make use of some form of **interior mutability** through the +//! `Cell` or `RefCell` types. + +#![macro_escape] +#![experimental] + +use prelude::*; + +use cell::UnsafeCell; + +// Sure wish we had macro hygiene, no? +#[doc(hidden)] pub use self::imp::Key as KeyInner; +#[doc(hidden)] pub use self::imp::destroy_value; +#[doc(hidden)] pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER; +#[doc(hidden)] pub use sys_common::thread_local::StaticKey as OsStaticKey; + +pub mod scoped; + +/// 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. +/// +/// The `with` method yields a reference to the contained value which cannot be +/// sent across tasks 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. +/// +/// # Example +/// +/// ``` +/// use std::cell::RefCell; +/// +/// thread_local!(static FOO: RefCell<uint> = RefCell::new(1)); +/// +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 1); +/// *f.borrow_mut() = 2; +/// }); +/// +/// // each thread starts out with the initial value of 1 +/// spawn(proc() { +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 1); +/// *f.borrow_mut() = 3; +/// }); +/// }); +/// +/// // we retain our original value of 2 despite the child thread +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 2); +/// }); +/// ``` +pub struct Key<T> { + // The key itself may be tagged with #[thread_local], and this `Key` is + // stored as a `static`, and it's not valid for a static to reference the + // address of another thread_local static. For this reason we kinda wonkily + // work around this by generating a shim function which will give us the + // address of the inner TLS key at runtime. + // + // This is trivially devirtualizable by LLVM because we never store anything + // to this field and rustc can declare the `static` as constant as well. + #[doc(hidden)] + pub inner: fn() -> &'static KeyInner<UnsafeCell<Option<T>>>, + + // initialization routine to invoke to create a value + #[doc(hidden)] + pub init: fn() -> T, +} + +/// Declare a new thread local storage key of type `std::thread_local::Key`. +#[macro_export] +#[doc(hidden)] +macro_rules! thread_local( + (static $name:ident: $t:ty = $init:expr) => ( + static $name: ::std::thread_local::Key<$t> = { + use std::cell::UnsafeCell as __UnsafeCell; + use std::thread_local::KeyInner as __KeyInner; + use std::option::Option as __Option; + use std::option::None as __None; + + __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { + __UnsafeCell { value: __None } + }) + fn __init() -> $t { $init } + fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { + &__KEY + } + ::std::thread_local::Key { inner: __getit, init: __init } + }; + ); + (pub static $name:ident: $t:ty = $init:expr) => ( + pub static $name: ::std::thread_local::Key<$t> = { + use std::cell::UnsafeCell as __UnsafeCell; + use std::thread_local::KeyInner as __KeyInner; + use std::option::Option as __Option; + use std::option::None as __None; + + __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { + __UnsafeCell { value: __None } + }) + fn __init() -> $t { $init } + fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { + &__KEY + } + ::std::thread_local::Key { inner: __getit, init: __init } + }; + ); +) + +// Macro pain #4586: +// +// When cross compiling, rustc will load plugins and macros from the *host* +// platform before search for macros from the target platform. This is primarily +// done to detect, for example, plugins. Ideally the macro below would be +// defined once per module below, but unfortunately this means we have the +// following situation: +// +// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro +// will inject #[thread_local] statics. +// 2. We then try to compile a program for arm-linux-androideabi +// 3. The compiler has a host of linux and a target of android, so it loads +// macros from the *linux* libstd. +// 4. The macro generates a #[thread_local] field, but the android libstd does +// not use #[thread_local] +// 5. Compile error about structs with wrong fields. +// +// To get around this, we're forced to inject the #[cfg] logic into the macro +// itself. Woohoo. + +#[macro_export] +macro_rules! __thread_local_inner( + (static $name:ident: $t:ty = $init:expr) => ( + #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)] + static $name: ::std::thread_local::KeyInner<$t> = + __thread_local_inner!($init, $t); + ); + (pub static $name:ident: $t:ty = $init:expr) => ( + #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)] + pub static $name: ::std::thread_local::KeyInner<$t> = + __thread_local_inner!($init, $t); + ); + ($init:expr, $t:ty) => ({ + #[cfg(any(target_os = "macos", target_os = "linux"))] + const INIT: ::std::thread_local::KeyInner<$t> = { + ::std::thread_local::KeyInner { + inner: ::std::cell::UnsafeCell { value: $init }, + dtor_registered: ::std::cell::UnsafeCell { value: false }, + dtor_running: ::std::cell::UnsafeCell { value: false }, + marker: ::std::kinds::marker::NoCopy, + } + }; + + #[cfg(not(any(target_os = "macos", target_os = "linux")))] + const INIT: ::std::thread_local::KeyInner<$t> = { + unsafe extern fn __destroy(ptr: *mut u8) { + ::std::thread_local::destroy_value::<$t>(ptr); + } + ::std::thread_local::KeyInner { + inner: ::std::cell::UnsafeCell { value: $init }, + os: ::std::thread_local::OsStaticKey { + inner: ::std::thread_local::OS_INIT_INNER, + dtor: ::std::option::Some(__destroy), + }, + } + }; + + INIT + }); +) + +impl<T: 'static> Key<T> { + /// Acquire a reference to the value in this TLS key. + /// + /// This will lazily initialize the value if this thread has not referenced + /// this key yet. + /// + /// # Panics + /// + /// This function will `panic!()` if the key currently has its + /// destructor running, and it **may** panic if the destructor has + /// previously been run for this thread. + pub fn with<R>(&'static self, f: |&T| -> R) -> R { + let slot = (self.inner)(); + unsafe { + let slot = slot.get().expect("cannot access a TLS value during or \ + after it is destroyed"); + if (*slot.get()).is_none() { + *slot.get() = Some((self.init)()); + } + f((*slot.get()).as_ref().unwrap()) + } + } + + /// Test this TLS key to determine whether its value has been destroyed for + /// the current thread or not. + /// + /// This will not initialize the key if it is not already initialized. + pub fn destroyed(&'static self) -> bool { + unsafe { (self.inner)().get().is_none() } + } +} + +#[cfg(any(target_os = "macos", target_os = "linux"))] +mod imp { + use prelude::*; + + use cell::UnsafeCell; + use intrinsics; + use kinds::marker; + use ptr; + + #[doc(hidden)] + pub struct Key<T> { + // Place the inner bits in an `UnsafeCell` to currently get around the + // "only Sync statics" restriction. This allows any type to be placed in + // the cell. + // + // Note that all access requires `T: 'static` so it can't be a type with + // any borrowed pointers still. + pub inner: UnsafeCell<T>, + + // Metadata to keep track of the state of the destructor. Remember that + // these variables are thread-local, not global. + pub dtor_registered: UnsafeCell<bool>, // should be Cell + pub dtor_running: UnsafeCell<bool>, // should be Cell + + // These shouldn't be copied around. + pub marker: marker::NoCopy, + } + + #[doc(hidden)] + impl<T> Key<T> { + pub unsafe fn get(&'static self) -> Option<&'static T> { + if intrinsics::needs_drop::<T>() && *self.dtor_running.get() { + return None + } + self.register_dtor(); + Some(&*self.inner.get()) + } + + unsafe fn register_dtor(&self) { + if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() { + return + } + + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + *self.dtor_registered.get() = true; + } + } + + // Since what appears to be glibc 2.18 this symbol has been shipped which + // GCC and clang both use to invoke destructors in thread_local globals, so + // let's do the same! + // + // Note, however, that we run on lots older linuxes, as well as cross + // compiling from a newer linux to an older linux, so we also have a + // fallback implementation to use as well. + // + // Due to rust-lang/rust#18804, make sure this is not generic! + #[cfg(target_os = "linux")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + use mem; + use libc; + use sys_common::thread_local as os; + + extern { + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: *const (); + } + if !__cxa_thread_atexit_impl.is_null() { + type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), + arg: *mut u8, + dso_handle: *mut u8) -> libc::c_int; + mem::transmute::<*const (), F>(__cxa_thread_atexit_impl) + (dtor, t, __dso_handle); + return + } + + // The fallback implementation uses a vanilla OS-based TLS key to track + // the list of destructors that need to be run for this thread. The key + // then has its own destructor which runs all the other destructors. + // + // The destructor for DTORS is a little special in that it has a `while` + // loop to continuously drain the list of registered destructors. It + // *should* be the case that this loop always terminates because we + // provide the guarantee that a TLS key cannot be set after it is + // flagged for destruction. + static DTORS: os::StaticKey = os::StaticKey { + inner: os::INIT_INNER, + dtor: Some(run_dtors), + }; + type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(mem::transmute(v)); + } + let list: &mut List = &mut *(DTORS.get() as *mut List); + list.push((t, dtor)); + + unsafe extern fn run_dtors(mut ptr: *mut u8) { + while !ptr.is_null() { + let list: Box<List> = mem::transmute(ptr); + for &(ptr, dtor) in list.iter() { + dtor(ptr); + } + ptr = DTORS.get(); + DTORS.set(0 as *mut _); + } + } + } + + // OSX's analog of the above linux function is this _tlv_atexit function. + // The disassembly of thread_local globals in C++ (at least produced by + // clang) will have this show up in the output. + #[cfg(target_os = "macos")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + extern { + fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), + arg: *mut u8); + } + _tlv_atexit(dtor, t); + } + + #[doc(hidden)] + pub 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.get() = true; + ptr::read((*ptr).inner.get() as *const T); + } +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +mod imp { + use prelude::*; + + use cell::UnsafeCell; + use mem; + use sys_common::thread_local::StaticKey as OsStaticKey; + + #[doc(hidden)] + pub struct Key<T> { + // Statically allocated initialization expression, using an `UnsafeCell` + // for the same reasons as above. + pub inner: UnsafeCell<T>, + + // OS-TLS key that we'll use to key off. + pub os: OsStaticKey, + } + + struct Value<T: 'static> { + key: &'static Key<T>, + value: T, + } + + #[doc(hidden)] + impl<T> Key<T> { + pub unsafe fn get(&'static self) -> Option<&'static T> { + self.ptr().map(|p| &*p) + } + + unsafe fn ptr(&'static self) -> Option<*mut T> { + let ptr = self.os.get() as *mut Value<T>; + if !ptr.is_null() { + if ptr as uint == 1 { + return None + } + return Some(&mut (*ptr).value as *mut T); + } + + // If the lookup returned null, we haven't initialized our own local + // copy, so do that now. + // + // Also note that this transmute_copy should be ok because the value + // `inner` is already validated to be a valid `static` value, so we + // should be able to freely copy the bits. + let ptr: Box<Value<T>> = box Value { + key: self, + value: mem::transmute_copy(&self.inner), + }; + let ptr: *mut Value<T> = mem::transmute(ptr); + self.os.set(ptr as *mut u8); + Some(&mut (*ptr).value as *mut T) + } + } + + #[doc(hidden)] + pub 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 + // `None`. + // + // Note that to prevent an infinite loop we reset it back to null right + // before we return from the destructor ourselves. + let ptr: Box<Value<T>> = mem::transmute(ptr); + let key = ptr.key; + key.os.set(1 as *mut u8); + drop(ptr); + key.os.set(0 as *mut u8); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use cell::UnsafeCell; + use rustrt::thread::Thread; + + struct Foo(Sender<()>); + + impl Drop for Foo { + fn drop(&mut self) { + let Foo(ref s) = *self; + s.send(()); + } + } + + #[test] + fn smoke_no_dtor() { + thread_local!(static FOO: UnsafeCell<int> = UnsafeCell { value: 1 }) + + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 1); + *f.get() = 2; + }); + let (tx, rx) = channel(); + spawn(proc() { + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 1); + }); + tx.send(()); + }); + rx.recv(); + + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 2); + }); + } + + #[test] + fn smoke_dtor() { + thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell { + value: None + }) + + let (tx, rx) = channel(); + spawn(proc() unsafe { + let mut tx = Some(tx); + FOO.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); + }); + rx.recv(); + } + + #[test] + fn circular() { + struct S1; + struct S2; + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }) + thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell { + value: None + }) + static mut HITS: uint = 0; + + impl Drop for S1 { + fn drop(&mut self) { + unsafe { + HITS += 1; + if K2.destroyed() { + assert_eq!(HITS, 3); + } else { + if HITS == 1 { + K2.with(|s| *s.get() = Some(S2)); + } else { + assert_eq!(HITS, 3); + } + } + } + } + } + impl Drop for S2 { + fn drop(&mut self) { + unsafe { + HITS += 1; + assert!(!K1.destroyed()); + assert_eq!(HITS, 2); + K1.with(|s| *s.get() = Some(S1)); + } + } + } + + Thread::start(proc() { + drop(S1); + }).join(); + } + + #[test] + fn self_referential() { + struct S1; + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }) + + impl Drop for S1 { + fn drop(&mut self) { + assert!(K1.destroyed()); + } + } + + Thread::start(proc() unsafe { + K1.with(|s| *s.get() = Some(S1)); + }).join(); + } + + #[test] + fn dtors_in_dtors_in_dtors() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }) + thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell { + value: None + }) + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + if !K2.destroyed() { + K2.with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + } + + let (tx, rx) = channel(); + spawn(proc() unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv(); + } +} + +#[cfg(test)] +mod dynamic_tests { + use prelude::*; + + use cell::RefCell; + use collections::HashMap; + + #[test] + fn smoke() { + fn square(i: int) -> int { i * i } + thread_local!(static FOO: int = square(3)) + + FOO.with(|f| { + assert_eq!(*f, 9); + }); + } + + #[test] + fn hashmap() { + fn map() -> RefCell<HashMap<int, int>> { + let mut m = HashMap::new(); + m.insert(1, 2); + RefCell::new(m) + } + thread_local!(static FOO: RefCell<HashMap<int, int>> = map()) + + FOO.with(|map| { + assert_eq!(map.borrow()[1], 2); + }); + } + + #[test] + fn refcell_vec() { + thread_local!(static FOO: RefCell<Vec<uint>> = RefCell::new(vec![1, 2, 3])) + + FOO.with(|vec| { + assert_eq!(vec.borrow().len(), 3); + vec.borrow_mut().push(4); + assert_eq!(vec.borrow()[3], 4); + }); + } +} diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs new file mode 100644 index 00000000000..11d539c4f9f --- /dev/null +++ b/src/libstd/thread_local/scoped.rs @@ -0,0 +1,261 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Scoped thread-local storage +//! +//! This module provides the ability to generate *scoped* thread-local +//! variables. In this sense, scoped indicates that thread local storage +//! actually stores a reference to a value, and this reference is only placed +//! in storage for a scoped amount of time. +//! +//! There are no restrictions on what types can be placed into a scoped +//! variable, but all scoped variables are initialized to the equivalent of +//! null. Scoped thread local stor is useful when a value is present for a known +//! period of time and it is not required to relinquish ownership of the +//! contents. +//! +//! # Example +//! +//! ``` +//! scoped_thread_local!(static FOO: uint) +//! +//! // Initially each scoped slot is empty. +//! assert!(!FOO.is_set()); +//! +//! // When inserting a value, the value is only in place for the duration +//! // of the closure specified. +//! FOO.set(&1, || { +//! FOO.with(|slot| { +//! assert_eq!(*slot, 1); +//! }); +//! }); +//! ``` + +#![macro_escape] + +use prelude::*; + +// macro hygiene sure would be nice, wouldn't it? +#[doc(hidden)] pub use self::imp::KeyInner; +#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT; + +/// Type representing a thread local storage key corresponding to a reference +/// to the type parameter `T`. +/// +/// Keys are statically allocated and can contain a reference to an instance of +/// type `T` scoped to a particular lifetime. Keys provides two methods, `set` +/// and `with`, both of which currently use closures to control the scope of +/// their contents. +pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> } + +/// Declare a new scoped thread local storage key. +/// +/// This macro declares a `static` item on which methods are used to get and +/// set the value stored within. +#[macro_export] +macro_rules! scoped_thread_local( + (static $name:ident: $t:ty) => ( + __scoped_thread_local_inner!(static $name: $t) + ); + (pub static $name:ident: $t:ty) => ( + __scoped_thread_local_inner!(pub static $name: $t) + ); +) + +#[macro_export] +#[doc(hidden)] +macro_rules! __scoped_thread_local_inner( + (static $name:ident: $t:ty) => ( + #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")), + thread_local)] + static $name: ::std::thread_local::scoped::Key<$t> = + __scoped_thread_local_inner!($t); + ); + (pub static $name:ident: $t:ty) => ( + #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")), + thread_local)] + pub static $name: ::std::thread_local::scoped::Key<$t> = + __scoped_thread_local_inner!($t); + ); + ($t:ty) => ({ + use std::thread_local::scoped::Key as __Key; + + #[cfg(not(any(windows, target_os = "android", target_os = "ios")))] + const INIT: __Key<$t> = __Key { + inner: ::std::thread_local::scoped::KeyInner { + inner: ::std::cell::UnsafeCell { value: 0 as *mut _ }, + } + }; + + #[cfg(any(windows, target_os = "android", target_os = "ios"))] + const INIT: __Key<$t> = __Key { + inner: ::std::thread_local::scoped::KeyInner { + inner: ::std::thread_local::scoped::OS_INIT, + marker: ::std::kinds::marker::InvariantType, + } + }; + + INIT + }) +) + +impl<T> Key<T> { + /// Insert a value into this scoped thread local storage slot for a + /// duration of a closure. + /// + /// While `cb` is running, the value `t` will be returned by `get` unless + /// this function is called recursively inside of `cb`. + /// + /// Upon return, this function will restore the previous value, if any + /// was available. + /// + /// # Example + /// + /// ``` + /// scoped_thread_local!(static FOO: uint) + /// + /// FOO.set(&100, || { + /// let val = FOO.with(|v| *v); + /// assert_eq!(val, 100); + /// + /// // set can be called recursively + /// FOO.set(&101, || { + /// // ... + /// }); + /// + /// // Recursive calls restore the previous value. + /// let val = FOO.with(|v| *v); + /// assert_eq!(val, 100); + /// }); + /// ``` + pub fn set<R>(&'static self, t: &T, cb: || -> R) -> R { + struct Reset<'a, T: 'a> { + key: &'a KeyInner<T>, + val: *mut T, + } + #[unsafe_destructor] + impl<'a, T> Drop for Reset<'a, T> { + fn drop(&mut self) { + unsafe { self.key.set(self.val) } + } + } + + let prev = unsafe { + let prev = self.inner.get(); + self.inner.set(t as *const T as *mut T); + prev + }; + + let _reset = Reset { key: &self.inner, val: prev }; + cb() + } + + /// Get a value out of this scoped variable. + /// + /// This function takes a closure which receives the value of this + /// variable. + /// + /// # Panics + /// + /// This function will panic if `set` has not previously been called. + /// + /// # Example + /// + /// ```no_run + /// scoped_thread_local!(static FOO: uint) + /// + /// FOO.with(|slot| { + /// // work with `slot` + /// }); + /// ``` + pub fn with<R>(&'static self, cb: |&T| -> R) -> R { + unsafe { + let ptr = self.inner.get(); + assert!(!ptr.is_null(), "cannot access a scoped thread local \ + variable without calling `set` first"); + cb(&*ptr) + } + } + + /// Test whether this TLS key has been `set` for the current thread. + pub fn is_set(&'static self) -> bool { + unsafe { !self.inner.get().is_null() } + } +} + +#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] +mod imp { + use std::cell::UnsafeCell; + + // FIXME: Should be a `Cell`, but that's not `Sync` + #[doc(hidden)] + pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> } + + #[doc(hidden)] + impl<T> KeyInner<T> { + #[doc(hidden)] + pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; } + #[doc(hidden)] + pub unsafe fn get(&self) -> *mut T { *self.inner.get() } + } +} + +#[cfg(any(windows, target_os = "android", target_os = "ios"))] +mod imp { + use kinds::marker; + use sys_common::thread_local::StaticKey as OsStaticKey; + + #[doc(hidden)] + pub struct KeyInner<T> { + pub inner: OsStaticKey, + pub marker: marker::InvariantType<T>, + } + + #[doc(hidden)] + impl<T> KeyInner<T> { + #[doc(hidden)] + pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) } + #[doc(hidden)] + pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ } + } +} + + +#[cfg(test)] +mod tests { + use cell::Cell; + use prelude::*; + + #[test] + fn smoke() { + scoped_thread_local!(static BAR: uint) + + assert!(!BAR.is_set()); + BAR.set(&1, || { + assert!(BAR.is_set()); + BAR.with(|slot| { + assert_eq!(*slot, 1); + }); + }); + assert!(!BAR.is_set()); + } + + #[test] + fn cell_allowed() { + scoped_thread_local!(static BAR: Cell<uint>) + + BAR.set(&Cell::new(1), || { + BAR.with(|slot| { + assert_eq!(slot.get(), 1); + }); + }); + } +} + |
