From 23fbe936bfa28fc2c16ec5e321cbe293ef044c0a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jul 2013 10:35:18 -0700 Subject: [PATCH 1/4] Allow non-uppercase-statics by default I think of this as a stylistic opinion which shouldn't necessarily be enforced by default on all users of rust, but that's just my opinion. --- src/librustc/middle/lint.rs | 2 +- src/librustc/rustc.rs | 2 -- src/libsyntax/syntax.rs | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 83eb2db2fe77c..b679da714a174 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -209,7 +209,7 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[ LintSpec { lint: non_uppercase_statics, desc: "static constants should have uppercase identifiers", - default: warn + default: allow }), ("managed_heap_memory", diff --git a/src/librustc/rustc.rs b/src/librustc/rustc.rs index 1e5e9ece288e8..787d8d5685a34 100644 --- a/src/librustc/rustc.rs +++ b/src/librustc/rustc.rs @@ -18,8 +18,6 @@ #[crate_type = "lib"]; #[allow(non_implicitly_copyable_typarams)]; -#[allow(non_camel_case_types)]; -#[allow(non_uppercase_statics)]; #[deny(deprecated_pattern)]; extern mod extra; diff --git a/src/libsyntax/syntax.rs b/src/libsyntax/syntax.rs index 7cb211a402b49..7a1e9923ab720 100644 --- a/src/libsyntax/syntax.rs +++ b/src/libsyntax/syntax.rs @@ -20,8 +20,6 @@ #[license = "MIT/ASL2"]; #[crate_type = "lib"]; -#[allow(non_camel_case_types)]; -#[allow(non_uppercase_statics)]; #[deny(deprecated_pattern)]; extern mod extra; From 242606c79339e2d2e3cf15cc8f02bd6ebc0b8c44 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jul 2013 01:56:40 -0700 Subject: [PATCH 2/4] Clean up various warnings throughout the codebase --- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/callee.rs | 18 ++++++------ src/librustc/middle/trans/controlflow.rs | 2 +- src/librustc/middle/ty.rs | 36 +++++++++++++----------- src/librustc/middle/typeck/mod.rs | 2 +- src/libsyntax/ext/pipes/proto.rs | 3 +- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 1173fa4941963..6d3f5d50b4b7f 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1428,7 +1428,7 @@ pub fn with_scope(bcx: block, pub fn with_scope_result(bcx: block, opt_node_info: Option, - name: &str, + _name: &str, f: &fn(block) -> Result) -> Result { let _icx = push_ctxt("with_scope_result"); diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 7b7989879a639..42f6a4dad6b74 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -197,7 +197,7 @@ fn get_impl_resolutions(bcx: block, impl_id: ast::def_id) -> typeck::vtable_res { if impl_id.crate == ast::local_crate { - *bcx.ccx().maps.vtable_map.get(&impl_id.node) + bcx.ccx().maps.vtable_map.get_copy(&impl_id.node) } else { // XXX: This is a temporary hack to work around not properly // exporting information about resolutions for impls. @@ -670,15 +670,13 @@ pub fn trans_call_inner(in_cx: block, None => { assert!(ty::type_is_immediate(bcx.tcx(), ret_ty)) } Some(expr::Ignore) => { // drop the value if it is not being saved. - unsafe { - if ty::type_needs_drop(bcx.tcx(), ret_ty) { - if ty::type_is_immediate(bcx.tcx(), ret_ty) { - let llscratchptr = alloc_ty(bcx, ret_ty, "__ret"); - Store(bcx, llresult, llscratchptr); - bcx = glue::drop_ty(bcx, llscratchptr, ret_ty); - } else { - bcx = glue::drop_ty(bcx, llretslot, ret_ty); - } + if ty::type_needs_drop(bcx.tcx(), ret_ty) { + if ty::type_is_immediate(bcx.tcx(), ret_ty) { + let llscratchptr = alloc_ty(bcx, ret_ty, "__ret"); + Store(bcx, llresult, llscratchptr); + bcx = glue::drop_ty(bcx, llscratchptr, ret_ty); + } else { + bcx = glue::drop_ty(bcx, llretslot, ret_ty); } } } diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 81260428f241b..e03a6e7c167cc 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -254,7 +254,7 @@ pub fn trans_break_cont(bcx: block, // Locate closest loop block, outputting cleanup as we go. let mut unwind = bcx; let mut cur_scope = unwind.scope; - let mut target = unwind; + let mut target; loop { cur_scope = match cur_scope { Some(@scope_info { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index a79f08b33c937..8e23f5431bcee 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3621,25 +3621,29 @@ pub fn trait_method_def_ids(cx: ctxt, id: ast::def_id) -> @~[def_id] { } pub fn impl_trait_ref(cx: ctxt, id: ast::def_id) -> Option<@TraitRef> { - *do cx.impl_trait_cache.find_or_insert_with(id) |_| { - if id.crate == ast::local_crate { - debug!("(impl_trait_ref) searching for trait impl %?", id); - match cx.items.find(&id.node) { - Some(&ast_map::node_item(@ast::item { - node: ast::item_impl(_, ref opt_trait, _, _), - _}, - _)) => { - match opt_trait { - &Some(ref t) => Some(ty::node_id_to_trait_ref(cx, t.ref_id)), - &None => None - } + match cx.impl_trait_cache.find(&id) { + Some(&ret) => { return ret; } + None => {} + } + let ret = if id.crate == ast::local_crate { + debug!("(impl_trait_ref) searching for trait impl %?", id); + match cx.items.find(&id.node) { + Some(&ast_map::node_item(@ast::item { + node: ast::item_impl(_, ref opt_trait, _, _), + _}, + _)) => { + match opt_trait { + &Some(ref t) => Some(ty::node_id_to_trait_ref(cx, t.ref_id)), + &None => None } - _ => None } - } else { - csearch::get_impl_trait(cx, id) + _ => None } - } + } else { + csearch::get_impl_trait(cx, id) + }; + cx.impl_trait_cache.insert(id, ret); + return ret; } pub fn ty_to_def_id(ty: t) -> Option { diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 6f5dde74b5c10..b8b8ab3145d54 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -303,7 +303,7 @@ fn check_main_fn_ty(ccx: &CrateCtxt, let tcx = ccx.tcx; let main_t = ty::node_id_to_type(tcx, main_id); match ty::get(main_t).sty { - ty::ty_bare_fn(ref fn_ty) => { + ty::ty_bare_fn(*) => { match tcx.items.find(&main_id) { Some(&ast_map::node_item(it,_)) => { match it.node { diff --git a/src/libsyntax/ext/pipes/proto.rs b/src/libsyntax/ext/pipes/proto.rs index 75424b60390d7..5866b8a5af55d 100644 --- a/src/libsyntax/ext/pipes/proto.rs +++ b/src/libsyntax/ext/pipes/proto.rs @@ -144,7 +144,8 @@ pub struct protocol_ { impl protocol_ { /// Get a state. pub fn get_state(&self, name: &str) -> state { - *self.states.iter().find_(|i| name == i.name).get() + let mut i = self.states.iter(); + *i.find_(|i| name == i.name).get() } pub fn get_state_by_id(&self, id: uint) -> state { self.states[id] } From e3211fa1f1f24268b91b0c89cb312e70499d41f3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jul 2013 01:38:44 -0700 Subject: [PATCH 3/4] Purge the last remnants of the old TLS api Closes #3273 --- src/libextra/rl.rs | 26 +- src/libextra/sort.rs | 35 --- src/librustc/middle/trans/base.rs | 41 ++- src/librustc/middle/trans/context.rs | 14 +- src/librusti/program.rs | 24 +- src/libstd/condition.rs | 83 ++++-- src/libstd/local_data.rs | 240 +++++++++--------- src/libstd/os.rs | 17 +- src/libstd/rand.rs | 16 +- src/libstd/rt/task.rs | 14 +- src/libstd/task/local_data_priv.rs | 7 +- src/libsyntax/ast_util.rs | 24 +- src/libsyntax/ext/expand.rs | 12 +- src/libsyntax/parse/token.rs | 23 +- .../compile-fail/core-tls-store-pointer.rs | 8 +- 15 files changed, 291 insertions(+), 293 deletions(-) diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs index c1eeb5005b265..b7b74694475a4 100644 --- a/src/libextra/rl.rs +++ b/src/libextra/rl.rs @@ -66,24 +66,28 @@ pub unsafe fn read(prompt: &str) -> Option<~str> { } } -pub type CompletionCb<'self> = @fn(~str, &'self fn(~str)); +pub type CompletionCb = @fn(~str, @fn(~str)); -fn complete_key(_v: @CompletionCb) {} +#[cfg(not(stage0))] +static complete_key: local_data::Key<@CompletionCb> = &[]; +#[cfg(stage0)] +fn complete_key(_: @CompletionCb) {} /// Bind to the main completion callback pub unsafe fn complete(cb: CompletionCb) { - local_data::set(complete_key, @(cb)); + local_data::set(complete_key, @cb); extern fn callback(line: *c_char, completions: *()) { - unsafe { - let cb = *local_data::get(complete_key, |k| k.map(|&k| *k)) - .get(); - - do cb(str::raw::from_c_str(line)) |suggestion| { - do str::as_c_str(suggestion) |buf| { - rustrt::linenoiseAddCompletion(completions, buf); + do local_data::get(complete_key) |cb| { + let cb = **cb.unwrap(); + + unsafe { + do cb(str::raw::from_c_str(line)) |suggestion| { + do str::as_c_str(suggestion) |buf| { + rustrt::linenoiseAddCompletion(completions, buf); + } } - } +} } } diff --git a/src/libextra/sort.rs b/src/libextra/sort.rs index d4d6162a9198f..db4ad43e41d7a 100644 --- a/src/libextra/sort.rs +++ b/src/libextra/sort.rs @@ -1195,39 +1195,4 @@ mod big_tests { isSorted(arr); } } - - struct LVal<'self> { - val: uint, - key: &'self fn:Copy(@uint), - } - - #[unsafe_destructor] - impl<'self> Drop for LVal<'self> { - fn drop(&self) { - let x = unsafe { local_data::get(self.key, |k| k.map(|&k| *k)) }; - match x { - Some(@y) => { - unsafe { - local_data::set(self.key, @(y+1)); - } - } - _ => fail!("Expected key to work"), - } - } - } - - impl<'self> Ord for LVal<'self> { - fn lt<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val < other.val - } - fn le<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val <= other.val - } - fn gt<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val > other.val - } - fn ge<'a>(&self, other: &'a LVal<'self>) -> bool { - (*self).val >= other.val - } - } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 6d3f5d50b4b7f..bfa325a21eef5 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -87,21 +87,20 @@ use syntax::abi::{X86, X86_64, Arm, Mips}; pub use middle::trans::context::task_llcx; -fn task_local_insn_key(_v: @~[&'static str]) {} +#[cfg(not(stage0))] +static task_local_insn_key: local_data::Key<@~[&'static str]> = &[]; +#[cfg(stage0)] +fn task_local_insn_key(_: @~[&'static str]) {} pub fn with_insn_ctxt(blk: &fn(&[&'static str])) { - unsafe { - let opt = local_data::get(task_local_insn_key, |k| k.map(|&k| *k)); - if opt.is_some() { - blk(*opt.unwrap()); - } + let opt = local_data::get(task_local_insn_key, |k| k.map(|&k| *k)); + if opt.is_some() { + blk(*opt.unwrap()); } } pub fn init_insn_ctxt() { - unsafe { - local_data::set(task_local_insn_key, @~[]); - } + local_data::set(task_local_insn_key, @~[]); } pub struct _InsnCtxt { _x: () } @@ -109,13 +108,11 @@ pub struct _InsnCtxt { _x: () } #[unsafe_destructor] impl Drop for _InsnCtxt { fn drop(&self) { - unsafe { - do local_data::modify(task_local_insn_key) |c| { - do c.map_consume |ctx| { - let mut ctx = copy *ctx; - ctx.pop(); - @ctx - } + do local_data::modify(task_local_insn_key) |c| { + do c.map_consume |ctx| { + let mut ctx = copy *ctx; + ctx.pop(); + @ctx } } } @@ -123,13 +120,11 @@ impl Drop for _InsnCtxt { pub fn push_ctxt(s: &'static str) -> _InsnCtxt { debug!("new InsnCtxt: %s", s); - unsafe { - do local_data::modify(task_local_insn_key) |c| { - do c.map_consume |ctx| { - let mut ctx = copy *ctx; - ctx.push(s); - @ctx - } + do local_data::modify(task_local_insn_key) |c| { + do c.map_consume |ctx| { + let mut ctx = copy *ctx; + ctx.push(s); + @ctx } } _InsnCtxt { _x: () } diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 1557916658df7..a2f0fd480e1d4 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -232,22 +232,24 @@ impl CrateContext { #[unsafe_destructor] impl Drop for CrateContext { fn drop(&self) { - unsafe { - unset_task_llcx(); - } + unset_task_llcx(); } } +#[cfg(stage0)] fn task_local_llcx_key(_v: @ContextRef) {} +#[cfg(not(stage0))] +static task_local_llcx_key: local_data::Key<@ContextRef> = &[]; + pub fn task_llcx() -> ContextRef { - let opt = unsafe { local_data::get(task_local_llcx_key, |k| k.map(|&k| *k)) }; + let opt = local_data::get(task_local_llcx_key, |k| k.map(|&k| *k)); *opt.expect("task-local LLVMContextRef wasn't ever set!") } -unsafe fn set_task_llcx(c: ContextRef) { +fn set_task_llcx(c: ContextRef) { local_data::set(task_local_llcx_key, @c); } -unsafe fn unset_task_llcx() { +fn unset_task_llcx() { local_data::pop(task_local_llcx_key); } diff --git a/src/librusti/program.rs b/src/librusti/program.rs index 716c7a2481eed..d1d5b47ff57f3 100644 --- a/src/librusti/program.rs +++ b/src/librusti/program.rs @@ -8,10 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cast; use std::hashmap::HashMap; use std::local_data; -use std::sys; +use std::vec; use syntax::ast; use syntax::parse::token; @@ -58,7 +57,7 @@ struct LocalVariable { } type LocalCache = @mut HashMap<~str, @~[u8]>; -fn tls_key(_k: LocalCache) {} +static tls_key: local_data::Key = &[]; impl Program { pub fn new() -> Program { @@ -131,21 +130,18 @@ impl Program { fn main() { "); - let key: sys::Closure = unsafe { - let tls_key: &'static fn(LocalCache) = tls_key; - cast::transmute(tls_key) - }; + let key: *LocalCache = vec::raw::to_ptr(tls_key); // First, get a handle to the tls map which stores all the local // variables. This works by totally legitimately using the 'code' // pointer of the 'tls_key' function as a uint, and then casting it back // up to a function code.push_str(fmt!(" let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe { - let key = ::std::sys::Closure{ code: %? as *(), - env: ::std::ptr::null() }; + let key = ::std::vec::raw::SliceRepr{ data: %? as *u8, + len: 0 }; let key = ::std::cast::transmute(key); ::std::local_data::get(key, |k| k.map(|&x| *x)).unwrap() - };\n", key.code as uint)); + };\n", key as uint)); // Using this __tls_map handle, deserialize each variable binding that // we know about @@ -226,18 +222,14 @@ impl Program { for self.local_vars.iter().advance |(name, value)| { map.insert(copy *name, @copy value.data); } - unsafe { - local_data::set(tls_key, map); - } + local_data::set(tls_key, map); } /// Once the program has finished running, this function will consume the /// task-local cache of local variables. After the program finishes running, /// it updates this cache with the new values of each local variable. pub fn consume_cache(&mut self) { - let map = unsafe { - local_data::pop(tls_key).expect("tls is empty") - }; + let map = local_data::pop(tls_key).expect("tls is empty"); do map.consume |name, value| { match self.local_vars.find_mut(&name) { Some(v) => { v.data = copy *value; } diff --git a/src/libstd/condition.rs b/src/libstd/condition.rs index d6d09527f8301..3a5be12b3c586 100644 --- a/src/libstd/condition.rs +++ b/src/libstd/condition.rs @@ -23,13 +23,59 @@ pub struct Handler { prev: Option<@Handler>, } +#[cfg(stage0)] pub struct Condition<'self, T, U> { name: &'static str, key: local_data::Key<'self, @Handler> } +#[cfg(not(stage0))] +pub struct Condition { + name: &'static str, + key: local_data::Key<@Handler> +} + +#[cfg(not(stage0))] +impl Condition { + pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> { + unsafe { + let p : *RustClosure = ::cast::transmute(&h); + let prev = local_data::get(self.key, |k| k.map(|&x| *x)); + let h = @Handler { handle: *p, prev: prev }; + Trap { cond: self, handler: h } + } + } + + pub fn raise(&self, t: T) -> U { + let msg = fmt!("Unhandled condition: %s: %?", self.name, t); + self.raise_default(t, || fail!(copy msg)) + } + pub fn raise_default(&self, t: T, default: &fn() -> U) -> U { + unsafe { + match local_data::pop(self.key) { + None => { + debug!("Condition.raise: found no handler"); + default() + } + Some(handler) => { + debug!("Condition.raise: found handler"); + match handler.prev { + None => {} + Some(hp) => local_data::set(self.key, hp) + } + let handle : &fn(T) -> U = + ::cast::transmute(handler.handle); + let u = handle(t); + local_data::set(self.key, handler); + u + } + } + } + } +} +#[cfg(stage0)] impl<'self, T, U> Condition<'self, T, U> { - pub fn trap(&'self self, h: &'self fn(T) -> U) -> Trap<'self, T, U> { + pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> { unsafe { let p : *RustClosure = ::cast::transmute(&h); let prev = local_data::get(self.key, |k| k.map(|&x| *x)); @@ -67,38 +113,45 @@ impl<'self, T, U> Condition<'self, T, U> { } } +#[cfg(stage0)] struct Trap<'self, T, U> { cond: &'self Condition<'self, T, U>, handler: @Handler } +#[cfg(not(stage0))] +struct Trap<'self, T, U> { + cond: &'self Condition, + handler: @Handler +} impl<'self, T, U> Trap<'self, T, U> { pub fn in(&self, inner: &'self fn() -> V) -> V { - unsafe { - let _g = Guard { cond: self.cond }; - debug!("Trap: pushing handler to TLS"); - local_data::set(self.cond.key, self.handler); - inner() - } + let _g = Guard { cond: self.cond }; + debug!("Trap: pushing handler to TLS"); + local_data::set(self.cond.key, self.handler); + inner() } } +#[cfg(stage0)] struct Guard<'self, T, U> { cond: &'self Condition<'self, T, U> } +#[cfg(not(stage0))] +struct Guard<'self, T, U> { + cond: &'self Condition +} #[unsafe_destructor] impl<'self, T, U> Drop for Guard<'self, T, U> { fn drop(&self) { - unsafe { - debug!("Guard: popping handler from TLS"); - let curr = local_data::pop(self.cond.key); - match curr { + debug!("Guard: popping handler from TLS"); + let curr = local_data::pop(self.cond.key); + match curr { + None => {} + Some(h) => match h.prev { None => {} - Some(h) => match h.prev { - None => {} - Some(hp) => local_data::set(self.cond.key, hp) - } + Some(hp) => local_data::set(self.cond.key, hp) } } } diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index b241de887004c..640bcc757b3fb 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -12,27 +12,25 @@ Task local data management -Allows storing boxes with arbitrary types inside, to be accessed anywhere within -a task, keyed by a pointer to a global finaliser function. Useful for dynamic -variables, singletons, and interfacing with foreign code with bad callback -interfaces. +Allows storing arbitrary types inside task-local-storage (TLS), to be accessed +anywhere within a task, keyed by a global slice of the appropriate type. +Useful for dynamic variables, singletons, and interfacing with foreign code +with bad callback interfaces. -To use, declare a monomorphic (no type parameters) global function at the type -to store, and use it as the 'key' when accessing. +To use, declare a static slice of the type you wish to store. The initialization +should be `&[]`. This is then the key to what you wish to store. ~~~{.rust} use std::local_data; -fn key_int(_: @int) {} -fn key_vector(_: @~[int]) {} +static key_int: local_data::Key = &[]; +static key_vector: local_data::Key<~[int]> = &[]; -unsafe { - local_data::set(key_int, @3); - assert!(local_data::get(key_int) == Some(@3)); +local_data::set(key_int, 3); +local_data::get(key_int, |opt| assert_eq!(opt, Some(&3))); - local_data::set(key_vector, @~[3]); - assert!(local_data::get(key_vector).unwrap()[0] == 3); -} +local_data::set(key_vector, ~[4]); +local_data::get(key_int, |opt| assert_eq!(opt, Some(&~[4]))); ~~~ Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation @@ -60,6 +58,9 @@ use task::local_data_priv::{local_get, local_pop, local_set, Handle}; * * These two cases aside, the interface is safe. */ +#[cfg(not(stage0))] +pub type Key = &'static [T]; +#[cfg(stage0)] pub type Key<'self,T> = &'self fn:Copy(v: T); /** @@ -67,56 +68,55 @@ pub type Key<'self,T> = &'self fn:Copy(v: T); * reference that was originally created to insert it. */ #[cfg(stage0)] -pub unsafe fn pop(key: Key<@T>) -> Option<@T> { - local_pop(Handle::new(), key) +pub fn pop(key: Key<@T>) -> Option<@T> { + unsafe { local_pop(Handle::new(), key) } } /** * Remove a task-local data value from the table, returning the * reference that was originally created to insert it. */ #[cfg(not(stage0))] -pub unsafe fn pop(key: Key) -> Option { - local_pop(Handle::new(), key) +pub fn pop(key: Key) -> Option { + unsafe { local_pop(Handle::new(), key) } } /** * Retrieve a task-local data value. It will also be kept alive in the * table until explicitly removed. */ #[cfg(stage0)] -pub unsafe fn get(key: Key<@T>, f: &fn(Option<&@T>) -> U) -> U { - local_get(Handle::new(), key, f) +pub fn get(key: Key<@T>, f: &fn(Option<&@T>) -> U) -> U { + unsafe { local_get(Handle::new(), key, f) } } /** * Retrieve a task-local data value. It will also be kept alive in the * table until explicitly removed. */ #[cfg(not(stage0))] -pub unsafe fn get(key: Key, f: &fn(Option<&T>) -> U) -> U { - local_get(Handle::new(), key, f) +pub fn get(key: Key, f: &fn(Option<&T>) -> U) -> U { + unsafe { local_get(Handle::new(), key, f) } } /** * Store a value in task-local data. If this key already has a value, * that value is overwritten (and its destructor is run). */ #[cfg(stage0)] -pub unsafe fn set(key: Key<@T>, data: @T) { - local_set(Handle::new(), key, data) +pub fn set(key: Key<@T>, data: @T) { + unsafe { local_set(Handle::new(), key, data) } } /** * Store a value in task-local data. If this key already has a value, * that value is overwritten (and its destructor is run). */ #[cfg(not(stage0))] -pub unsafe fn set(key: Key, data: T) { - local_set(Handle::new(), key, data) +pub fn set(key: Key, data: T) { + unsafe { local_set(Handle::new(), key, data) } } /** * Modify a task-local data value. If the function returns 'None', the * data is removed (and its reference dropped). */ #[cfg(stage0)] -pub unsafe fn modify(key: Key<@T>, - f: &fn(Option<@T>) -> Option<@T>) { +pub fn modify(key: Key<@T>, f: &fn(Option<@T>) -> Option<@T>) { match f(pop(key)) { Some(next) => { set(key, next); } None => {} @@ -127,8 +127,7 @@ pub unsafe fn modify(key: Key<@T>, * data is removed (and its reference dropped). */ #[cfg(not(stage0))] -pub unsafe fn modify(key: Key, - f: &fn(Option) -> Option) { +pub fn modify(key: Key, f: &fn(Option) -> Option) { match f(pop(key)) { Some(next) => { set(key, next); } None => {} @@ -137,64 +136,56 @@ pub unsafe fn modify(key: Key, #[test] fn test_tls_multitask() { - unsafe { - fn my_key(_x: @~str) { } - set(my_key, @~"parent data"); - do task::spawn { - // TLS shouldn't carry over. - assert!(get(my_key, |k| k.map(|&k| *k)).is_none()); - set(my_key, @~"child data"); - assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == - ~"child data"); - // should be cleaned up for us - } - // Must work multiple times - assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); - assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); - assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); + static my_key: Key<@~str> = &[]; + set(my_key, @~"parent data"); + do task::spawn { + // TLS shouldn't carry over. + assert!(get(my_key, |k| k.map(|&k| *k)).is_none()); + set(my_key, @~"child data"); + assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == + ~"child data"); + // should be cleaned up for us } + // Must work multiple times + assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); + assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); + assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"parent data"); } #[test] fn test_tls_overwrite() { - unsafe { - fn my_key(_x: @~str) { } - set(my_key, @~"first data"); - set(my_key, @~"next data"); // Shouldn't leak. - assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"next data"); - } + static my_key: Key<@~str> = &[]; + set(my_key, @~"first data"); + set(my_key, @~"next data"); // Shouldn't leak. + assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"next data"); } #[test] fn test_tls_pop() { - unsafe { - fn my_key(_x: @~str) { } - set(my_key, @~"weasel"); - assert!(*(pop(my_key).get()) == ~"weasel"); - // Pop must remove the data from the map. - assert!(pop(my_key).is_none()); - } + static my_key: Key<@~str> = &[]; + set(my_key, @~"weasel"); + assert!(*(pop(my_key).get()) == ~"weasel"); + // Pop must remove the data from the map. + assert!(pop(my_key).is_none()); } #[test] fn test_tls_modify() { - unsafe { - fn my_key(_x: @~str) { } - modify(my_key, |data| { - match data { - Some(@ref val) => fail!("unwelcome value: %s", *val), - None => Some(@~"first data") - } - }); - modify(my_key, |data| { - match data { - Some(@~"first data") => Some(@~"next data"), - Some(@ref val) => fail!("wrong value: %s", *val), - None => fail!("missing value") - } - }); - assert!(*(pop(my_key).get()) == ~"next data"); - } + static my_key: Key<@~str> = &[]; + modify(my_key, |data| { + match data { + Some(@ref val) => fail!("unwelcome value: %s", *val), + None => Some(@~"first data") + } + }); + modify(my_key, |data| { + match data { + Some(@~"first data") => Some(@~"next data"), + Some(@ref val) => fail!("wrong value: %s", *val), + None => fail!("missing value") + } + }); + assert!(*(pop(my_key).get()) == ~"next data"); } #[test] @@ -205,40 +196,36 @@ fn test_tls_crust_automorestack_memorial_bug() { // to get recorded as something within a rust stack segment. Then a // subsequent upcall (esp. for logging, think vsnprintf) would run on // a stack smaller than 1 MB. - fn my_key(_x: @~str) { } + static my_key: Key<@~str> = &[]; do task::spawn { - unsafe { set(my_key, @~"hax"); } + set(my_key, @~"hax"); } } #[test] fn test_tls_multiple_types() { - fn str_key(_x: @~str) { } - fn box_key(_x: @@()) { } - fn int_key(_x: @int) { } + static str_key: Key<@~str> = &[]; + static box_key: Key<@@()> = &[]; + static int_key: Key<@int> = &[]; do task::spawn { - unsafe { - set(str_key, @~"string data"); - set(box_key, @@()); - set(int_key, @42); - } + set(str_key, @~"string data"); + set(box_key, @@()); + set(int_key, @42); } } #[test] fn test_tls_overwrite_multiple_types() { - fn str_key(_x: @~str) { } - fn box_key(_x: @@()) { } - fn int_key(_x: @int) { } + static str_key: Key<@~str> = &[]; + static box_key: Key<@@()> = &[]; + static int_key: Key<@int> = &[]; do task::spawn { - unsafe { - set(str_key, @~"string data"); - set(int_key, @42); - // This could cause a segfault if overwriting-destruction is done - // with the crazy polymorphic transmute rather than the provided - // finaliser. - set(int_key, @31337); - } + set(str_key, @~"string data"); + set(int_key, @42); + // This could cause a segfault if overwriting-destruction is done + // with the crazy polymorphic transmute rather than the provided + // finaliser. + set(int_key, @31337); } } @@ -246,38 +233,53 @@ fn test_tls_overwrite_multiple_types() { #[should_fail] #[ignore(cfg(windows))] fn test_tls_cleanup_on_failure() { - unsafe { - fn str_key(_x: @~str) { } - fn box_key(_x: @@()) { } - fn int_key(_x: @int) { } - set(str_key, @~"parent data"); + static str_key: Key<@~str> = &[]; + static box_key: Key<@@()> = &[]; + static int_key: Key<@int> = &[]; + set(str_key, @~"parent data"); + set(box_key, @@()); + do task::spawn { + // spawn_linked + set(str_key, @~"string data"); set(box_key, @@()); - do task::spawn { - // spawn_linked - set(str_key, @~"string data"); - set(box_key, @@()); - set(int_key, @42); - fail!(); - } - // Not quite nondeterministic. - set(int_key, @31337); + set(int_key, @42); fail!(); } + // Not quite nondeterministic. + set(int_key, @31337); + fail!(); } #[test] fn test_static_pointer() { - unsafe { - fn key(_x: @&'static int) { } - static VALUE: int = 0; - set(key, @&VALUE); - } + static key: Key<@&'static int> = &[]; + static VALUE: int = 0; + let v: @&'static int = @&VALUE; + set(key, v); } #[test] fn test_owned() { - unsafe { - fn key(_x: ~int) { } - set(key, ~1); - } + static key: Key<~int> = &[]; + set(key, ~1); +} + +#[test] +fn test_same_key_type() { + static key1: Key = &[]; + static key2: Key = &[]; + static key3: Key = &[]; + static key4: Key = &[]; + static key5: Key = &[]; + set(key1, 1); + set(key2, 2); + set(key3, 3); + set(key4, 4); + set(key5, 5); + + get(key1, |x| assert_eq!(*x.unwrap(), 1)); + get(key2, |x| assert_eq!(*x.unwrap(), 2)); + get(key3, |x| assert_eq!(*x.unwrap(), 3)); + get(key4, |x| assert_eq!(*x.unwrap(), 4)); + get(key5, |x| assert_eq!(*x.unwrap(), 5)); } diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 8d70732641d8c..cbd1e4e7663f6 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1239,7 +1239,10 @@ struct OverriddenArgs { val: ~[~str] } +#[cfg(stage0)] fn overridden_arg_key(_v: @OverriddenArgs) {} +#[cfg(not(stage0))] +static overridden_arg_key: local_data::Key<@OverriddenArgs> = &[]; /// Returns the arguments which this program was started with (normally passed /// via the command line). @@ -1247,11 +1250,9 @@ fn overridden_arg_key(_v: @OverriddenArgs) {} /// The return value of the function can be changed by invoking the /// `os::set_args` function. pub fn args() -> ~[~str] { - unsafe { - match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) { - None => real_args(), - Some(args) => copy args.val - } + match local_data::get(overridden_arg_key, |k| k.map(|&k| *k)) { + None => real_args(), + Some(args) => copy args.val } } @@ -1259,10 +1260,8 @@ pub fn args() -> ~[~str] { /// program had when it started. These new arguments are only available to the /// current task via the `os::args` method. pub fn set_args(new_args: ~[~str]) { - unsafe { - let overridden_args = @OverriddenArgs { val: copy new_args }; - local_data::set(overridden_arg_key, overridden_args); - } + let overridden_args = @OverriddenArgs { val: copy new_args }; + local_data::set(overridden_arg_key, overridden_args); } // FIXME #6100 we should really use an internal implementation of this - using diff --git a/src/libstd/rand.rs b/src/libstd/rand.rs index 2cf45ba70ec0e..8551012d6d77a 100644 --- a/src/libstd/rand.rs +++ b/src/libstd/rand.rs @@ -851,7 +851,10 @@ pub fn seed() -> ~[u8] { } // used to make space in TLS for a random number generator +#[cfg(stage0)] fn tls_rng_state(_v: @@mut IsaacRng) {} +#[cfg(not(stage0))] +static tls_rng_state: local_data::Key<@@mut IsaacRng> = &[]; /** * Gives back a lazily initialized task-local random number generator, @@ -860,17 +863,12 @@ fn tls_rng_state(_v: @@mut IsaacRng) {} */ #[inline] pub fn task_rng() -> @mut IsaacRng { - let r : Option<@@mut IsaacRng>; - unsafe { - r = local_data::get(tls_rng_state, |k| k.map(|&k| *k)); - } + let r = local_data::get(tls_rng_state, |k| k.map(|&k| *k)); match r { None => { - unsafe { - let rng = @@mut IsaacRng::new_seeded(seed()); - local_data::set(tls_rng_state, rng); - *rng - } + let rng = @@mut IsaacRng::new_seeded(seed()); + local_data::set(tls_rng_state, rng); + *rng } Some(rng) => *rng } diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index c5961be40ecbe..0fd8c5c03d339 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -348,14 +348,12 @@ mod test { fn tls() { use local_data; do run_in_newsched_task() { - unsafe { - fn key(_x: @~str) { } - local_data::set(key, @~"data"); - assert!(*local_data::get(key, |k| k.map(|&k| *k)).get() == ~"data"); - fn key2(_x: @~str) { } - local_data::set(key2, @~"data"); - assert!(*local_data::get(key2, |k| k.map(|&k| *k)).get() == ~"data"); - } + static key: local_data::Key<@~str> = &[]; + local_data::set(key, @~"data"); + assert!(*local_data::get(key, |k| k.map(|&k| *k)).get() == ~"data"); + static key2: local_data::Key<@~str> = &[]; + local_data::set(key2, @~"data"); + assert!(*local_data::get(key2, |k| k.map(|&k| *k)).get() == ~"data"); } } diff --git a/src/libstd/task/local_data_priv.rs b/src/libstd/task/local_data_priv.rs index 42cfcbc16dbf8..1a2141e996a48 100644 --- a/src/libstd/task/local_data_priv.rs +++ b/src/libstd/task/local_data_priv.rs @@ -15,9 +15,9 @@ use libc; use local_data; use prelude::*; use ptr; -use sys; use task::rt; use util; +use vec; use super::rt::rust_task; use rt::task::{Task, LocalStorage}; @@ -142,9 +142,8 @@ unsafe fn get_local_map(handle: Handle) -> &mut TaskLocalMap { } } -unsafe fn key_to_key_value(key: local_data::Key) -> *libc::c_void { - let pair: sys::Closure = cast::transmute(key); - return pair.code as *libc::c_void; +fn key_to_key_value(key: local_data::Key) -> *libc::c_void { + return vec::raw::to_ptr(key) as *libc::c_void; } pub unsafe fn local_pop(handle: Handle, diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 7567dc0000be1..0b876bf1f817c 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -20,7 +20,6 @@ use std::hashmap::HashMap; use std::int; use std::num; use std::option; -use std::cast; use std::local_data; pub fn path_name_i(idents: &[ident]) -> ~str { @@ -695,18 +694,17 @@ pub fn new_sctable_internal() -> SCTable { // fetch the SCTable from TLS, create one if it doesn't yet exist. pub fn get_sctable() -> @mut SCTable { - unsafe { - let sctable_key = (cast::transmute::<(uint, uint), - &fn:Copy(v: @@mut SCTable)>( - (-4 as uint, 0u))); - match local_data::get(sctable_key, |k| k.map(|&k| *k)) { - None => { - let new_table = @@mut new_sctable_internal(); - local_data::set(sctable_key,new_table); - *new_table - }, - Some(intr) => *intr - } + #[cfg(not(stage0))] + static sctable_key: local_data::Key<@@mut SCTable> = &[]; + #[cfg(stage0)] + fn sctable_key(_: @@mut SCTable) {} + match local_data::get(sctable_key, |k| k.map(|&k| *k)) { + None => { + let new_table = @@mut new_sctable_internal(); + local_data::set(sctable_key,new_table); + *new_table + }, + Some(intr) => *intr } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 2c3f42cca9aac..4fbc386284892 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -579,11 +579,11 @@ pub fn core_macros() -> @str { { pub $c:ident: $in:ty -> $out:ty; } => { pub mod $c { - fn key(_x: @::std::condition::Handler<$in,$out>) { } + #[allow(non_uppercase_statics)]; + static key: &'static [@::std::condition::Handler<$in, $out>] = &[]; - #[allow(non_uppercase_statics)] pub static cond : - ::std::condition::Condition<'static,$in,$out> = + ::std::condition::Condition<$in,$out> = ::std::condition::Condition { name: stringify!($c), key: key @@ -595,11 +595,11 @@ pub fn core_macros() -> @str { // FIXME (#6009): remove mod's `pub` below once variant above lands. pub mod $c { - fn key(_x: @::std::condition::Handler<$in,$out>) { } + #[allow(non_uppercase_statics)]; + static key: &'static [@::std::condition::Handler<$in, $out>] = &[]; - #[allow(non_uppercase_statics)] pub static cond : - ::std::condition::Condition<'static,$in,$out> = + ::std::condition::Condition<$in,$out> = ::std::condition::Condition { name: stringify!($c), key: key diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 46e0ef32321a5..8737e571399be 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -15,7 +15,6 @@ use parse::token; use util::interner::StrInterner; use util::interner; -use std::cast; use std::cmp::Equiv; use std::local_data; use std::rand; @@ -485,18 +484,16 @@ fn mk_fresh_ident_interner() -> @ident_interner { // if an interner exists in TLS, return it. Otherwise, prepare a // fresh one. pub fn get_ident_interner() -> @ident_interner { - unsafe { - let key = - (cast::transmute::<(uint, uint), - &fn:Copy(v: @@::parse::token::ident_interner)>( - (-3 as uint, 0u))); - match local_data::get(key, |k| k.map(|&k| *k)) { - Some(interner) => *interner, - None => { - let interner = mk_fresh_ident_interner(); - local_data::set(key, @interner); - interner - } + #[cfg(not(stage0))] + static key: local_data::Key<@@::parse::token::ident_interner> = &[]; + #[cfg(stage0)] + fn key(_: @@::parse::token::ident_interner) {} + match local_data::get(key, |k| k.map(|&k| *k)) { + Some(interner) => *interner, + None => { + let interner = mk_fresh_ident_interner(); + local_data::set(key, @interner); + interner } } } diff --git a/src/test/compile-fail/core-tls-store-pointer.rs b/src/test/compile-fail/core-tls-store-pointer.rs index 13c9966922889..f5b7f34b36530 100644 --- a/src/test/compile-fail/core-tls-store-pointer.rs +++ b/src/test/compile-fail/core-tls-store-pointer.rs @@ -12,10 +12,6 @@ use std::local_data; -fn key(_x: @&int) { } +static key: local_data::Key<@&int> = &[]; //~ ERROR only 'static is allowed -fn main() { - unsafe { - local_data::set(key, @&0); //~ ERROR does not fulfill `'static` - } -} +fn main() {} From 9fd2ac7428afa4f414f32b8b4876ca817ee85f16 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 14 Jul 2013 01:43:31 -0700 Subject: [PATCH 4/4] Make TLS keys actually take up space If the TLS key is 0-sized, then the linux linker is apparently smart enough to put everything at the same pointer. OSX on the other hand, will reserve some space for all of them. To get around this, the TLS key now actuall consumes space to ensure that it gets a unique pointer --- src/libextra/rl.rs | 2 +- src/librustc/back/link.rs | 8 +- src/librustc/middle/trans/base.rs | 2 +- src/librustc/middle/trans/context.rs | 2 +- src/librusti/program.rs | 10 +-- src/libstd/local_data.rs | 80 +++++++++---------- src/libstd/os.rs | 2 +- src/libstd/rand.rs | 2 +- src/libstd/rt/task.rs | 4 +- src/libstd/std.rs | 1 + src/libstd/task/local_data_priv.rs | 3 +- src/libstd/task/spawn.rs | 19 +++-- src/libsyntax/ast_util.rs | 2 +- src/libsyntax/ext/expand.rs | 8 +- src/libsyntax/parse/token.rs | 3 +- .../compile-fail/core-tls-store-pointer.rs | 3 +- 16 files changed, 81 insertions(+), 70 deletions(-) diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs index b7b74694475a4..59801c945b626 100644 --- a/src/libextra/rl.rs +++ b/src/libextra/rl.rs @@ -69,7 +69,7 @@ pub unsafe fn read(prompt: &str) -> Option<~str> { pub type CompletionCb = @fn(~str, @fn(~str)); #[cfg(not(stage0))] -static complete_key: local_data::Key<@CompletionCb> = &[]; +static complete_key: local_data::Key<@CompletionCb> = &local_data::Key; #[cfg(stage0)] fn complete_key(_: @CompletionCb) {} diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 593a1ed535d6b..aeed2d842c131 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -105,6 +105,7 @@ pub mod jit { use metadata::cstore; use std::cast; + #[cfg(not(stage0))] use std::local_data; use std::unstable::intrinsics; @@ -202,18 +203,19 @@ pub mod jit { // The stage1 compiler won't work, but that doesn't really matter. TLS // changed only very recently to allow storage of owned values. - fn engine_key(_: ~Engine) {} + #[cfg(not(stage0))] + static engine_key: local_data::Key<~Engine> = &local_data::Key; #[cfg(not(stage0))] fn set_engine(engine: ~Engine) { - unsafe { local_data::set(engine_key, engine) } + local_data::set(engine_key, engine) } #[cfg(stage0)] fn set_engine(_: ~Engine) {} #[cfg(not(stage0))] pub fn consume_engine() -> Option<~Engine> { - unsafe { local_data::pop(engine_key) } + local_data::pop(engine_key) } #[cfg(stage0)] pub fn consume_engine() -> Option<~Engine> { None } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index bfa325a21eef5..7182f7ff8b7d4 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -88,7 +88,7 @@ use syntax::abi::{X86, X86_64, Arm, Mips}; pub use middle::trans::context::task_llcx; #[cfg(not(stage0))] -static task_local_insn_key: local_data::Key<@~[&'static str]> = &[]; +static task_local_insn_key: local_data::Key<@~[&'static str]> = &local_data::Key; #[cfg(stage0)] fn task_local_insn_key(_: @~[&'static str]) {} diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index a2f0fd480e1d4..ffebb87d5cf04 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -239,7 +239,7 @@ impl Drop for CrateContext { #[cfg(stage0)] fn task_local_llcx_key(_v: @ContextRef) {} #[cfg(not(stage0))] -static task_local_llcx_key: local_data::Key<@ContextRef> = &[]; +static task_local_llcx_key: local_data::Key<@ContextRef> = &local_data::Key; pub fn task_llcx() -> ContextRef { let opt = local_data::get(task_local_llcx_key, |k| k.map(|&k| *k)); diff --git a/src/librusti/program.rs b/src/librusti/program.rs index d1d5b47ff57f3..9031a001eca60 100644 --- a/src/librusti/program.rs +++ b/src/librusti/program.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cast; use std::hashmap::HashMap; use std::local_data; -use std::vec; use syntax::ast; use syntax::parse::token; @@ -57,7 +57,7 @@ struct LocalVariable { } type LocalCache = @mut HashMap<~str, @~[u8]>; -static tls_key: local_data::Key = &[]; +static tls_key: local_data::Key = &local_data::Key; impl Program { pub fn new() -> Program { @@ -130,16 +130,14 @@ impl Program { fn main() { "); - let key: *LocalCache = vec::raw::to_ptr(tls_key); + let key: uint= unsafe { cast::transmute(tls_key) }; // First, get a handle to the tls map which stores all the local // variables. This works by totally legitimately using the 'code' // pointer of the 'tls_key' function as a uint, and then casting it back // up to a function code.push_str(fmt!(" let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe { - let key = ::std::vec::raw::SliceRepr{ data: %? as *u8, - len: 0 }; - let key = ::std::cast::transmute(key); + let key = ::std::cast::transmute(%u); ::std::local_data::get(key, |k| k.map(|&x| *x)).unwrap() };\n", key as uint)); diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index 640bcc757b3fb..be170cce07e54 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -13,18 +13,19 @@ Task local data management Allows storing arbitrary types inside task-local-storage (TLS), to be accessed -anywhere within a task, keyed by a global slice of the appropriate type. -Useful for dynamic variables, singletons, and interfacing with foreign code -with bad callback interfaces. +anywhere within a task, keyed by a global pointer parameterized over the type of +the TLS slot. Useful for dynamic variables, singletons, and interfacing with +foreign code with bad callback interfaces. -To use, declare a static slice of the type you wish to store. The initialization -should be `&[]`. This is then the key to what you wish to store. +To use, declare a static variable of the type you wish to store. The +initialization should be `&local_data::Key`. This is then the key to what you +wish to store. ~~~{.rust} use std::local_data; -static key_int: local_data::Key = &[]; -static key_vector: local_data::Key<~[int]> = &[]; +static key_int: local_data::Key = &local_data::Key; +static key_vector: local_data::Key<~[int]> = &local_data::Key; local_data::set(key_int, 3); local_data::get(key_int, |opt| assert_eq!(opt, Some(&3))); @@ -45,24 +46,23 @@ use task::local_data_priv::{local_get, local_pop, local_set, Handle}; #[cfg(test)] use task; /** - * Indexes a task-local data slot. The function's code pointer is used for - * comparison. Recommended use is to write an empty function for each desired - * task-local data slot (and use class destructors, not code inside the - * function, if specific teardown is needed). DO NOT use multiple - * instantiations of a single polymorphic function to index data of different - * types; arbitrary type coercion is possible this way. + * Indexes a task-local data slot. This pointer is used for comparison to + * differentiate keys from one another. The actual type `T` is not used anywhere + * as a member of this type, except that it is parameterized with it to define + * the type of each key's value. * - * One other exception is that this global state can be used in a destructor - * context to create a circular @-box reference, which will crash during task - * failure (see issue #3039). - * - * These two cases aside, the interface is safe. + * The value of each Key is of the singleton enum KeyValue. These also have the + * same name as `Key` and their purpose is to take up space in the programs data + * sections to ensure that each value of the `Key` type points to a unique + * location. */ #[cfg(not(stage0))] -pub type Key = &'static [T]; +pub type Key = &'static KeyValue; #[cfg(stage0)] pub type Key<'self,T> = &'self fn:Copy(v: T); +pub enum KeyValue { Key } + /** * Remove a task-local data value from the table, returning the * reference that was originally created to insert it. @@ -136,7 +136,7 @@ pub fn modify(key: Key, f: &fn(Option) -> Option) { #[test] fn test_tls_multitask() { - static my_key: Key<@~str> = &[]; + static my_key: Key<@~str> = &Key; set(my_key, @~"parent data"); do task::spawn { // TLS shouldn't carry over. @@ -154,7 +154,7 @@ fn test_tls_multitask() { #[test] fn test_tls_overwrite() { - static my_key: Key<@~str> = &[]; + static my_key: Key<@~str> = &Key; set(my_key, @~"first data"); set(my_key, @~"next data"); // Shouldn't leak. assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"next data"); @@ -162,7 +162,7 @@ fn test_tls_overwrite() { #[test] fn test_tls_pop() { - static my_key: Key<@~str> = &[]; + static my_key: Key<@~str> = &Key; set(my_key, @~"weasel"); assert!(*(pop(my_key).get()) == ~"weasel"); // Pop must remove the data from the map. @@ -171,7 +171,7 @@ fn test_tls_pop() { #[test] fn test_tls_modify() { - static my_key: Key<@~str> = &[]; + static my_key: Key<@~str> = &Key; modify(my_key, |data| { match data { Some(@ref val) => fail!("unwelcome value: %s", *val), @@ -196,7 +196,7 @@ fn test_tls_crust_automorestack_memorial_bug() { // to get recorded as something within a rust stack segment. Then a // subsequent upcall (esp. for logging, think vsnprintf) would run on // a stack smaller than 1 MB. - static my_key: Key<@~str> = &[]; + static my_key: Key<@~str> = &Key; do task::spawn { set(my_key, @~"hax"); } @@ -204,9 +204,9 @@ fn test_tls_crust_automorestack_memorial_bug() { #[test] fn test_tls_multiple_types() { - static str_key: Key<@~str> = &[]; - static box_key: Key<@@()> = &[]; - static int_key: Key<@int> = &[]; + static str_key: Key<@~str> = &Key; + static box_key: Key<@@()> = &Key; + static int_key: Key<@int> = &Key; do task::spawn { set(str_key, @~"string data"); set(box_key, @@()); @@ -216,9 +216,9 @@ fn test_tls_multiple_types() { #[test] fn test_tls_overwrite_multiple_types() { - static str_key: Key<@~str> = &[]; - static box_key: Key<@@()> = &[]; - static int_key: Key<@int> = &[]; + static str_key: Key<@~str> = &Key; + static box_key: Key<@@()> = &Key; + static int_key: Key<@int> = &Key; do task::spawn { set(str_key, @~"string data"); set(int_key, @42); @@ -233,9 +233,9 @@ fn test_tls_overwrite_multiple_types() { #[should_fail] #[ignore(cfg(windows))] fn test_tls_cleanup_on_failure() { - static str_key: Key<@~str> = &[]; - static box_key: Key<@@()> = &[]; - static int_key: Key<@int> = &[]; + static str_key: Key<@~str> = &Key; + static box_key: Key<@@()> = &Key; + static int_key: Key<@int> = &Key; set(str_key, @~"parent data"); set(box_key, @@()); do task::spawn { @@ -252,7 +252,7 @@ fn test_tls_cleanup_on_failure() { #[test] fn test_static_pointer() { - static key: Key<@&'static int> = &[]; + static key: Key<@&'static int> = &Key; static VALUE: int = 0; let v: @&'static int = @&VALUE; set(key, v); @@ -260,17 +260,17 @@ fn test_static_pointer() { #[test] fn test_owned() { - static key: Key<~int> = &[]; + static key: Key<~int> = &Key; set(key, ~1); } #[test] fn test_same_key_type() { - static key1: Key = &[]; - static key2: Key = &[]; - static key3: Key = &[]; - static key4: Key = &[]; - static key5: Key = &[]; + static key1: Key = &Key; + static key2: Key = &Key; + static key3: Key = &Key; + static key4: Key = &Key; + static key5: Key = &Key; set(key1, 1); set(key2, 2); set(key3, 3); diff --git a/src/libstd/os.rs b/src/libstd/os.rs index cbd1e4e7663f6..58175db1241d2 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1242,7 +1242,7 @@ struct OverriddenArgs { #[cfg(stage0)] fn overridden_arg_key(_v: @OverriddenArgs) {} #[cfg(not(stage0))] -static overridden_arg_key: local_data::Key<@OverriddenArgs> = &[]; +static overridden_arg_key: local_data::Key<@OverriddenArgs> = &local_data::Key; /// Returns the arguments which this program was started with (normally passed /// via the command line). diff --git a/src/libstd/rand.rs b/src/libstd/rand.rs index 8551012d6d77a..6f89e7ffb0794 100644 --- a/src/libstd/rand.rs +++ b/src/libstd/rand.rs @@ -854,7 +854,7 @@ pub fn seed() -> ~[u8] { #[cfg(stage0)] fn tls_rng_state(_v: @@mut IsaacRng) {} #[cfg(not(stage0))] -static tls_rng_state: local_data::Key<@@mut IsaacRng> = &[]; +static tls_rng_state: local_data::Key<@@mut IsaacRng> = &local_data::Key; /** * Gives back a lazily initialized task-local random number generator, diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 0fd8c5c03d339..17d0d59660f1b 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -348,10 +348,10 @@ mod test { fn tls() { use local_data; do run_in_newsched_task() { - static key: local_data::Key<@~str> = &[]; + static key: local_data::Key<@~str> = &local_data::Key; local_data::set(key, @~"data"); assert!(*local_data::get(key, |k| k.map(|&k| *k)).get() == ~"data"); - static key2: local_data::Key<@~str> = &[]; + static key2: local_data::Key<@~str> = &local_data::Key; local_data::set(key2, @~"data"); assert!(*local_data::get(key2, |k| k.map(|&k| *k)).get() == ~"data"); } diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 8f86216d24070..03b895b38601a 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -222,6 +222,7 @@ mod std { pub use condition; pub use option; pub use kinds; + pub use local_data; pub use sys; pub use pipes; pub use unstable; diff --git a/src/libstd/task/local_data_priv.rs b/src/libstd/task/local_data_priv.rs index 1a2141e996a48..d46e5707f14a9 100644 --- a/src/libstd/task/local_data_priv.rs +++ b/src/libstd/task/local_data_priv.rs @@ -17,7 +17,6 @@ use prelude::*; use ptr; use task::rt; use util; -use vec; use super::rt::rust_task; use rt::task::{Task, LocalStorage}; @@ -143,7 +142,7 @@ unsafe fn get_local_map(handle: Handle) -> &mut TaskLocalMap { } fn key_to_key_value(key: local_data::Key) -> *libc::c_void { - return vec::raw::to_ptr(key) as *libc::c_void; + unsafe { cast::transmute(key) } } pub unsafe fn local_pop(handle: Handle, diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index 27cb1c2c1000a..206d19e175fe9 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -80,6 +80,7 @@ use cell::Cell; use container::MutableMap; use comm::{Chan, GenericChan}; use hashmap::HashSet; +use local_data; use task::local_data_priv::{local_get, local_set, OldHandle}; use task::rt::rust_task; use task::rt; @@ -465,10 +466,14 @@ fn kill_taskgroup(state: TaskGroupInner, me: *rust_task, is_main: bool) { // FIXME (#2912): Work around core-vs-coretest function duplication. Can't use // a proper closure because the #[test]s won't understand. Have to fake it. -macro_rules! taskgroup_key ( - // Use a "code pointer" value that will never be a real code pointer. - () => (cast::transmute((-2 as uint, 0u))) -) +#[cfg(not(stage0))] +fn taskgroup_key() -> local_data::Key<@@mut TCB> { + unsafe { cast::transmute(-2) } +} +#[cfg(stage0)] +fn taskgroup_key() -> local_data::Key<@@mut TCB> { + unsafe { cast::transmute((-2, 0)) } +} fn gen_child_taskgroup(linked: bool, supervised: bool) -> (TaskGroupArc, AncestorList, bool) { @@ -478,7 +483,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) * Step 1. Get spawner's taskgroup info. *##################################################################*/ let spawner_group: @@mut TCB = - do local_get(OldHandle(spawner), taskgroup_key!()) |group| { + do local_get(OldHandle(spawner), taskgroup_key()) |group| { match group { None => { // Main task, doing first spawn ever. Lazily initialise @@ -495,7 +500,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool) AncestorList(None), true, None); - local_set(OldHandle(spawner), taskgroup_key!(), group); + local_set(OldHandle(spawner), taskgroup_key(), group); group } Some(&group) => group @@ -688,7 +693,7 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) { is_main, notifier); unsafe { - local_set(OldHandle(child), taskgroup_key!(), group); + local_set(OldHandle(child), taskgroup_key(), group); } // Run the child's body. diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 0b876bf1f817c..a1d209d19ac56 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -695,7 +695,7 @@ pub fn new_sctable_internal() -> SCTable { // fetch the SCTable from TLS, create one if it doesn't yet exist. pub fn get_sctable() -> @mut SCTable { #[cfg(not(stage0))] - static sctable_key: local_data::Key<@@mut SCTable> = &[]; + static sctable_key: local_data::Key<@@mut SCTable> = &local_data::Key; #[cfg(stage0)] fn sctable_key(_: @@mut SCTable) {} match local_data::get(sctable_key, |k| k.map(|&k| *k)) { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4fbc386284892..b45cde6a8e342 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -580,7 +580,9 @@ pub fn core_macros() -> @str { pub mod $c { #[allow(non_uppercase_statics)]; - static key: &'static [@::std::condition::Handler<$in, $out>] = &[]; + static key: ::std::local_data::Key< + @::std::condition::Handler<$in, $out>> = + &::std::local_data::Key; pub static cond : ::std::condition::Condition<$in,$out> = @@ -596,7 +598,9 @@ pub fn core_macros() -> @str { // FIXME (#6009): remove mod's `pub` below once variant above lands. pub mod $c { #[allow(non_uppercase_statics)]; - static key: &'static [@::std::condition::Handler<$in, $out>] = &[]; + static key: ::std::local_data::Key< + @::std::condition::Handler<$in, $out>> = + &::std::local_data::Key; pub static cond : ::std::condition::Condition<$in,$out> = diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 8737e571399be..01860c3ae995b 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -485,7 +485,8 @@ fn mk_fresh_ident_interner() -> @ident_interner { // fresh one. pub fn get_ident_interner() -> @ident_interner { #[cfg(not(stage0))] - static key: local_data::Key<@@::parse::token::ident_interner> = &[]; + static key: local_data::Key<@@::parse::token::ident_interner> = + &local_data::Key; #[cfg(stage0)] fn key(_: @@::parse::token::ident_interner) {} match local_data::get(key, |k| k.map(|&k| *k)) { diff --git a/src/test/compile-fail/core-tls-store-pointer.rs b/src/test/compile-fail/core-tls-store-pointer.rs index f5b7f34b36530..576b1c452a5ec 100644 --- a/src/test/compile-fail/core-tls-store-pointer.rs +++ b/src/test/compile-fail/core-tls-store-pointer.rs @@ -12,6 +12,7 @@ use std::local_data; -static key: local_data::Key<@&int> = &[]; //~ ERROR only 'static is allowed +static key: local_data::Key<@&int> = &local_data::Key; +//~^ ERROR only 'static is allowed fn main() {}