From 1795ae4e8a472ce500660fa59abf61114f5ef8c9 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 6 Nov 2013 00:38:08 -0500 Subject: [PATCH 1/3] add `#[thread_local]` attribute This provides a building block for fast thread-local storage. It does not change the safety semantics of `static mut`. Closes #10310 --- doc/rust.md | 2 ++ src/librustc/lib/llvm.rs | 6 ++++++ src/librustc/middle/lint.rs | 1 + src/librustc/middle/trans/base.rs | 4 ++++ 4 files changed, 13 insertions(+) diff --git a/doc/rust.md b/doc/rust.md index fe8d0a834e3..f15e767ca82 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1754,6 +1754,8 @@ names are effectively reserved. Some significant attributes include: * The `deriving` attribute, for automatically generating implementations of certain traits. * The `static_assert` attribute, for asserting that a static bool is true at compiletime +* The `thread_local` attribute, for defining a `static mut` as a thread-local. Note that this is + only a low-level building block, and is not local to a *task*, nor does it provide safety. Other attributes may be added or removed during development of the language. diff --git a/src/librustc/lib/llvm.rs b/src/librustc/lib/llvm.rs index 160bc511168..66f6e6a4746 100644 --- a/src/librustc/lib/llvm.rs +++ b/src/librustc/lib/llvm.rs @@ -1749,6 +1749,12 @@ pub fn SetUnnamedAddr(Global: ValueRef, Unnamed: bool) { } } +pub fn set_thread_local(global: ValueRef, is_thread_local: bool) { + unsafe { + llvm::LLVMSetThreadLocal(global, is_thread_local as Bool); + } +} + pub fn ConstICmp(Pred: IntPredicate, V1: ValueRef, V2: ValueRef) -> ValueRef { unsafe { llvm::LLVMConstICmp(Pred as c_ushort, V1, V2) diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 2b2c2aa74c7..a08afcfd7c5 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -816,6 +816,7 @@ static obsolete_attrs: &'static [(&'static str, &'static str)] = &[ static other_attrs: &'static [&'static str] = &[ // item-level "address_insignificant", // can be crate-level too + "thread_local", // for statics "allow", "deny", "forbid", "warn", // lint options "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze", diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 0ece7c8c024..20cc2f8944e 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2543,6 +2543,10 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { inlineable = true; } + if attr::contains_name(i.attrs, "thread_local") { + lib::llvm::set_thread_local(g, true); + } + if !inlineable { debug!("{} not inlined", sym); ccx.non_inlineable_statics.insert(id); From 2cf3d8adf2cbdf760063a247162a558e68fe35fd Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Wed, 6 Nov 2013 01:17:04 -0500 Subject: [PATCH 2/3] port the runtime to `#[thread_local]` --- src/libstd/rt/local_ptr.rs | 106 +++++++++++++++++++++++++++++++++++-- src/libstd/rt/mod.rs | 4 ++ 2 files changed, 105 insertions(+), 5 deletions(-) diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index 862ecd6499a..d840a6b2ff3 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -17,16 +17,35 @@ use libc::c_void; use cast; +#[cfg(stage0)] +#[cfg(windows)] use ptr; use cell::Cell; use option::{Option, Some, None}; use unstable::finally::Finally; +#[cfg(stage0)] +#[cfg(windows)] use unstable::mutex::{Mutex, MUTEX_INIT}; +#[cfg(stage0)] +#[cfg(windows)] use tls = rt::thread_local_storage; +#[cfg(not(stage0), not(windows), test)] +#[thread_local] +pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; + +#[cfg(not(stage0), not(windows), not(test))] +#[thread_local] +pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void; + +#[cfg(stage0)] +#[cfg(windows)] static mut RT_TLS_KEY: tls::Key = -1; /// Initialize the TLS key. Other ops will fail if this isn't executed first. +#[inline(never)] +#[cfg(stage0)] +#[cfg(windows)] pub fn init_tls_key() { static mut lock: Mutex = MUTEX_INIT; static mut initialized: bool = false; @@ -41,24 +60,42 @@ pub fn init_tls_key() { } } +#[cfg(not(stage0), not(windows))] +pub fn init_tls_key() {} + /// Give a pointer to thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] +#[cfg(stage0)] +#[cfg(windows)] pub unsafe fn put(sched: ~T) { let key = tls_key(); let void_ptr: *mut c_void = cast::transmute(sched); tls::set(key, void_ptr); } +/// Give a pointer to thread-local storage. +/// +/// # Safety note +/// +/// Does not validate the pointer type. +#[inline] +#[cfg(not(stage0), not(windows))] +pub unsafe fn put(sched: ~T) { + RT_TLS_PTR = cast::transmute(sched) +} + /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] +#[cfg(stage0)] +#[cfg(windows)] pub unsafe fn take() -> ~T { let key = tls_key(); let void_ptr: *mut c_void = tls::get(key); @@ -70,6 +107,19 @@ pub unsafe fn take() -> ~T { return ptr; } +/// Take ownership of a pointer from thread-local storage. +/// +/// # Safety note +/// +/// Does not validate the pointer type. +#[inline] +#[cfg(not(stage0), not(windows))] +pub unsafe fn take() -> ~T { + let ptr: ~T = cast::transmute(RT_TLS_PTR); + RT_TLS_PTR = cast::transmute(0); // can't use `as`, due to type not matching with `cfg(test)` + ptr +} + /// Take ownership of a pointer from thread-local storage. /// /// # Safety note @@ -77,6 +127,8 @@ pub unsafe fn take() -> ~T { /// Does not validate the pointer type. /// Leaves the old pointer in TLS for speed. #[inline] +#[cfg(stage0)] +#[cfg(windows)] pub unsafe fn unsafe_take() -> ~T { let key = tls_key(); let void_ptr: *mut c_void = tls::get(key); @@ -87,7 +139,21 @@ pub unsafe fn unsafe_take() -> ~T { return ptr; } +/// Take ownership of a pointer from thread-local storage. +/// +/// # Safety note +/// +/// Does not validate the pointer type. +/// Leaves the old pointer in TLS for speed. +#[inline] +#[cfg(not(stage0), not(windows))] +pub unsafe fn unsafe_take() -> ~T { + cast::transmute(RT_TLS_PTR) +} + /// Check whether there is a thread-local pointer installed. +#[cfg(stage0)] +#[cfg(windows)] pub fn exists() -> bool { unsafe { match maybe_tls_key() { @@ -97,6 +163,14 @@ pub fn exists() -> bool { } } +/// Check whether there is a thread-local pointer installed. +#[cfg(not(stage0), not(windows))] +pub fn exists() -> bool { + unsafe { + RT_TLS_PTR.is_not_null() + } +} + /// Borrow the thread-local value from thread-local storage. /// While the value is borrowed it is not available in TLS. /// @@ -123,6 +197,8 @@ pub unsafe fn borrow(f: |&mut T|) { /// /// Because this leaves the value in thread-local storage it is possible /// For the Scheduler pointer to be aliased +#[cfg(stage0)] +#[cfg(windows)] pub unsafe fn unsafe_borrow() -> *mut T { let key = tls_key(); let void_ptr = tls::get(key); @@ -132,6 +208,16 @@ pub unsafe fn unsafe_borrow() -> *mut T { void_ptr as *mut T } +#[cfg(not(stage0), not(windows))] +pub unsafe fn unsafe_borrow() -> *mut T { + if RT_TLS_PTR.is_null() { + rtabort!("thread-local pointer is null. bogus!"); + } + RT_TLS_PTR as *mut T +} + +#[cfg(stage0)] +#[cfg(windows)] pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { match maybe_tls_key() { Some(key) => { @@ -146,7 +232,18 @@ pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { } } +#[cfg(not(stage0), not(windows))] +pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { + if RT_TLS_PTR.is_null() { + None + } else { + Some(RT_TLS_PTR as *mut T) + } +} + #[inline] +#[cfg(stage0)] +#[cfg(windows)] fn tls_key() -> tls::Key { match maybe_tls_key() { Some(key) => key, @@ -155,7 +252,8 @@ fn tls_key() -> tls::Key { } #[inline] -#[cfg(not(test))] +#[cfg(not(test), stage0)] +#[cfg(not(test), windows)] pub fn maybe_tls_key() -> Option { unsafe { // NB: This is a little racy because, while the key is @@ -176,11 +274,9 @@ pub fn maybe_tls_key() -> Option { } } -// XXX: The boundary between the running runtime and the testing runtime -// seems to be fuzzy at the moment, and trying to use two different keys -// results in disaster. This should not be necessary. #[inline] -#[cfg(test)] +#[cfg(test, stage0)] +#[cfg(test, windows)] pub fn maybe_tls_key() -> Option { unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index ad5c69e9a0c..1d2742fd029 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -95,7 +95,11 @@ pub use self::kill::BlockedTask; pub mod shouldnt_be_public { pub use super::select::SelectInner; pub use super::select::{SelectInner, SelectPortInner}; + #[cfg(stage0)] + #[cfg(windows)] pub use super::local_ptr::maybe_tls_key; + #[cfg(not(stage0), not(windows))] + pub use super::local_ptr::RT_TLS_PTR; } // Internal macros used by the runtime. From a5af479bb479a2a4d3289f9feed9ac3ff299a395 Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Tue, 26 Nov 2013 14:21:22 -0500 Subject: [PATCH 3/3] add a thread_local feature gate --- src/librustc/front/feature_gate.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 6103312bc78..be35a309c44 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -38,6 +38,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("asm", Active), ("managed_boxes", Active), ("non_ascii_idents", Active), + ("thread_local", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -107,6 +108,17 @@ impl Visitor<()> for Context { } fn visit_item(&mut self, i: @ast::item, _:()) { + // NOTE: uncomment after snapshot + /* + for attr in i.attrs.iter() { + if "thread_local" == attr.name() { + self.gate_feature("thread_local", i.span, + "`#[thread_local]` is an experimental feature, and does not \ + currently handle destructors. There is no corresponding \ + `#[task_local]` mapping to the task model"); + } + } + */ match i.node { ast::item_enum(ref def, _) => { for variant in def.variants.iter() { @@ -152,8 +164,8 @@ impl Visitor<()> for Context { }, ast::ty_box(_) => { self.gate_feature("managed_boxes", t.span, - "The managed box syntax is being replaced by the `std::gc::Gc` - and `std::rc::Rc` types. Equivalent functionality to managed + "The managed box syntax is being replaced by the `std::gc::Gc` \ + and `std::rc::Rc` types. Equivalent functionality to managed \ trait objects will be implemented but is currently missing."); } _ => {}