auto merge of #10312 : thestinger/rust/thread_local, r=alexcritchton

This provides a building block for fast thread-local storage. It does
not change the safety semantics of `static mut`.

Closes #10310
This commit is contained in:
bors 2013-11-26 13:32:43 -08:00
commit 35ebf03489
7 changed files with 132 additions and 7 deletions

View File

@ -1754,6 +1754,8 @@ names are effectively reserved. Some significant attributes include:
* The `deriving` attribute, for automatically generating * The `deriving` attribute, for automatically generating
implementations of certain traits. implementations of certain traits.
* The `static_assert` attribute, for asserting that a static bool is true at compiletime * 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. Other attributes may be added or removed during development of the language.

View File

@ -38,6 +38,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
("asm", Active), ("asm", Active),
("managed_boxes", Active), ("managed_boxes", Active),
("non_ascii_idents", Active), ("non_ascii_idents", Active),
("thread_local", Active),
// These are used to test this portion of the compiler, they don't actually // These are used to test this portion of the compiler, they don't actually
// mean anything // mean anything
@ -107,6 +108,17 @@ impl Visitor<()> for Context {
} }
fn visit_item(&mut self, i: @ast::item, _:()) { 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 { match i.node {
ast::item_enum(ref def, _) => { ast::item_enum(ref def, _) => {
for variant in def.variants.iter() { for variant in def.variants.iter() {
@ -152,8 +164,8 @@ impl Visitor<()> for Context {
}, },
ast::ty_box(_) => { ast::ty_box(_) => {
self.gate_feature("managed_boxes", t.span, self.gate_feature("managed_boxes", t.span,
"The managed box syntax is being replaced by the `std::gc::Gc` "The managed box syntax is being replaced by the `std::gc::Gc` \
and `std::rc::Rc` types. Equivalent functionality to managed and `std::rc::Rc` types. Equivalent functionality to managed \
trait objects will be implemented but is currently missing."); trait objects will be implemented but is currently missing.");
} }
_ => {} _ => {}

View File

@ -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 { pub fn ConstICmp(Pred: IntPredicate, V1: ValueRef, V2: ValueRef) -> ValueRef {
unsafe { unsafe {
llvm::LLVMConstICmp(Pred as c_ushort, V1, V2) llvm::LLVMConstICmp(Pred as c_ushort, V1, V2)

View File

@ -816,6 +816,7 @@ static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
static other_attrs: &'static [&'static str] = &[ static other_attrs: &'static [&'static str] = &[
// item-level // item-level
"address_insignificant", // can be crate-level too "address_insignificant", // can be crate-level too
"thread_local", // for statics
"allow", "deny", "forbid", "warn", // lint options "allow", "deny", "forbid", "warn", // lint options
"deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
"crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze", "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze",

View File

@ -2544,6 +2544,10 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
inlineable = true; inlineable = true;
} }
if attr::contains_name(i.attrs, "thread_local") {
lib::llvm::set_thread_local(g, true);
}
if !inlineable { if !inlineable {
debug!("{} not inlined", sym); debug!("{} not inlined", sym);
ccx.non_inlineable_statics.insert(id); ccx.non_inlineable_statics.insert(id);

View File

@ -17,16 +17,35 @@
use libc::c_void; use libc::c_void;
use cast; use cast;
#[cfg(stage0)]
#[cfg(windows)]
use ptr; use ptr;
use cell::Cell; use cell::Cell;
use option::{Option, Some, None}; use option::{Option, Some, None};
use unstable::finally::Finally; use unstable::finally::Finally;
#[cfg(stage0)]
#[cfg(windows)]
use unstable::mutex::{Mutex, MUTEX_INIT}; use unstable::mutex::{Mutex, MUTEX_INIT};
#[cfg(stage0)]
#[cfg(windows)]
use tls = rt::thread_local_storage; 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; static mut RT_TLS_KEY: tls::Key = -1;
/// Initialize the TLS key. Other ops will fail if this isn't executed first. /// 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() { pub fn init_tls_key() {
static mut lock: Mutex = MUTEX_INIT; static mut lock: Mutex = MUTEX_INIT;
static mut initialized: bool = false; 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. /// Give a pointer to thread-local storage.
/// ///
/// # Safety note /// # Safety note
/// ///
/// Does not validate the pointer type. /// Does not validate the pointer type.
#[inline] #[inline]
#[cfg(stage0)]
#[cfg(windows)]
pub unsafe fn put<T>(sched: ~T) { pub unsafe fn put<T>(sched: ~T) {
let key = tls_key(); let key = tls_key();
let void_ptr: *mut c_void = cast::transmute(sched); let void_ptr: *mut c_void = cast::transmute(sched);
tls::set(key, void_ptr); 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<T>(sched: ~T) {
RT_TLS_PTR = cast::transmute(sched)
}
/// Take ownership of a pointer from thread-local storage. /// Take ownership of a pointer from thread-local storage.
/// ///
/// # Safety note /// # Safety note
/// ///
/// Does not validate the pointer type. /// Does not validate the pointer type.
#[inline] #[inline]
#[cfg(stage0)]
#[cfg(windows)]
pub unsafe fn take<T>() -> ~T { pub unsafe fn take<T>() -> ~T {
let key = tls_key(); let key = tls_key();
let void_ptr: *mut c_void = tls::get(key); let void_ptr: *mut c_void = tls::get(key);
@ -70,6 +107,19 @@ pub unsafe fn take<T>() -> ~T {
return ptr; 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>() -> ~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. /// Take ownership of a pointer from thread-local storage.
/// ///
/// # Safety note /// # Safety note
@ -77,6 +127,8 @@ pub unsafe fn take<T>() -> ~T {
/// Does not validate the pointer type. /// Does not validate the pointer type.
/// Leaves the old pointer in TLS for speed. /// Leaves the old pointer in TLS for speed.
#[inline] #[inline]
#[cfg(stage0)]
#[cfg(windows)]
pub unsafe fn unsafe_take<T>() -> ~T { pub unsafe fn unsafe_take<T>() -> ~T {
let key = tls_key(); let key = tls_key();
let void_ptr: *mut c_void = tls::get(key); let void_ptr: *mut c_void = tls::get(key);
@ -87,7 +139,21 @@ pub unsafe fn unsafe_take<T>() -> ~T {
return ptr; 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>() -> ~T {
cast::transmute(RT_TLS_PTR)
}
/// Check whether there is a thread-local pointer installed. /// Check whether there is a thread-local pointer installed.
#[cfg(stage0)]
#[cfg(windows)]
pub fn exists() -> bool { pub fn exists() -> bool {
unsafe { unsafe {
match maybe_tls_key() { 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. /// Borrow the thread-local value from thread-local storage.
/// While the value is borrowed it is not available in TLS. /// While the value is borrowed it is not available in TLS.
/// ///
@ -119,6 +193,8 @@ pub unsafe fn borrow<T>(f: |&mut T|) {
/// ///
/// Because this leaves the value in thread-local storage it is possible /// Because this leaves the value in thread-local storage it is possible
/// For the Scheduler pointer to be aliased /// For the Scheduler pointer to be aliased
#[cfg(stage0)]
#[cfg(windows)]
pub unsafe fn unsafe_borrow<T>() -> *mut T { pub unsafe fn unsafe_borrow<T>() -> *mut T {
let key = tls_key(); let key = tls_key();
let void_ptr = tls::get(key); let void_ptr = tls::get(key);
@ -128,6 +204,16 @@ pub unsafe fn unsafe_borrow<T>() -> *mut T {
void_ptr as *mut T void_ptr as *mut T
} }
#[cfg(not(stage0), not(windows))]
pub unsafe fn unsafe_borrow<T>() -> *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<T>() -> Option<*mut T> { pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
match maybe_tls_key() { match maybe_tls_key() {
Some(key) => { Some(key) => {
@ -142,7 +228,18 @@ pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
} }
} }
#[cfg(not(stage0), not(windows))]
pub unsafe fn try_unsafe_borrow<T>() -> Option<*mut T> {
if RT_TLS_PTR.is_null() {
None
} else {
Some(RT_TLS_PTR as *mut T)
}
}
#[inline] #[inline]
#[cfg(stage0)]
#[cfg(windows)]
fn tls_key() -> tls::Key { fn tls_key() -> tls::Key {
match maybe_tls_key() { match maybe_tls_key() {
Some(key) => key, Some(key) => key,
@ -151,7 +248,8 @@ fn tls_key() -> tls::Key {
} }
#[inline] #[inline]
#[cfg(not(test))] #[cfg(not(test), stage0)]
#[cfg(not(test), windows)]
pub fn maybe_tls_key() -> Option<tls::Key> { pub fn maybe_tls_key() -> Option<tls::Key> {
unsafe { unsafe {
// NB: This is a little racy because, while the key is // NB: This is a little racy because, while the key is
@ -172,11 +270,9 @@ pub fn maybe_tls_key() -> Option<tls::Key> {
} }
} }
// 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] #[inline]
#[cfg(test)] #[cfg(test, stage0)]
#[cfg(test, windows)]
pub fn maybe_tls_key() -> Option<tls::Key> { pub fn maybe_tls_key() -> Option<tls::Key> {
unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) }
} }

View File

@ -95,7 +95,11 @@ pub use self::kill::BlockedTask;
pub mod shouldnt_be_public { pub mod shouldnt_be_public {
pub use super::select::SelectInner; pub use super::select::SelectInner;
pub use super::select::{SelectInner, SelectPortInner}; pub use super::select::{SelectInner, SelectPortInner};
#[cfg(stage0)]
#[cfg(windows)]
pub use super::local_ptr::maybe_tls_key; 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. // Internal macros used by the runtime.