Skip to content

add #[thread_local] attribute #10312

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 26, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
16 changes: 14 additions & 2 deletions src/librustc/front/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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.");
}
_ => {}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/lib/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/librustc/middle/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
106 changes: 101 additions & 5 deletions src/libstd/rt/local_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -70,13 +107,28 @@ 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
///
/// 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);
Expand All @@ -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() {
Expand All @@ -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.
///
Expand All @@ -123,6 +197,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);
Expand All @@ -132,6 +208,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) => {
Expand All @@ -146,7 +232,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,
Expand All @@ -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<tls::Key> {
unsafe {
// NB: This is a little racy because, while the key is
Expand All @@ -176,11 +274,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()) }
}
4 changes: 4 additions & 0 deletions src/libstd/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down