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
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.

View File

@ -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.");
}
_ => {}

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 {
unsafe {
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] = &[
// 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",

View File

@ -2544,6 +2544,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);

View File

@ -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<T>(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<T>(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>() -> ~T {
let key = tls_key();
let void_ptr: *mut c_void = tls::get(key);
@ -70,6 +107,19 @@ pub unsafe fn take<T>() -> ~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>() -> ~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>() -> ~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>() -> ~T {
let key = tls_key();
let void_ptr: *mut c_void = tls::get(key);
@ -87,7 +139,21 @@ pub unsafe fn unsafe_take<T>() -> ~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>() -> ~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.
///
@ -119,6 +193,8 @@ pub unsafe fn borrow<T>(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<T>() -> *mut T {
let key = tls_key();
let void_ptr = tls::get(key);
@ -128,6 +204,16 @@ pub unsafe fn unsafe_borrow<T>() -> *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> {
match maybe_tls_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]
#[cfg(stage0)]
#[cfg(windows)]
fn tls_key() -> tls::Key {
match maybe_tls_key() {
Some(key) => key,
@ -151,7 +248,8 @@ fn tls_key() -> tls::Key {
}
#[inline]
#[cfg(not(test))]
#[cfg(not(test), stage0)]
#[cfg(not(test), windows)]
pub fn maybe_tls_key() -> Option<tls::Key> {
unsafe {
// 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]
#[cfg(test)]
#[cfg(test, stage0)]
#[cfg(test, windows)]
pub fn maybe_tls_key() -> Option<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 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.