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:
commit
35ebf03489
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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()) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue