From e3211fa1f1f24268b91b0c89cb312e70499d41f3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jul 2013 01:38:44 -0700 Subject: Purge the last remnants of the old TLS api Closes #3273 --- 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 +- 6 files changed, 213 insertions(+), 164 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/condition.rs b/src/libstd/condition.rs index d6d09527f83..3a5be12b3c5 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 b241de88700..640bcc757b3 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 8d70732641d..cbd1e4e7663 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 2cf45ba70ec..8551012d6d7 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 c5961be40ec..0fd8c5c03d3 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 42cfcbc16db..1a2141e996a 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, -- cgit 1.4.1-3-g733a5 From 9fd2ac7428afa4f414f32b8b4876ca817ee85f16 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 14 Jul 2013 01:43:31 -0700 Subject: 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 +- src/test/compile-fail/core-tls-store-pointer.rs | 3 +- 16 files changed, 81 insertions(+), 70 deletions(-) (limited to 'src/libstd') diff --git a/src/libextra/rl.rs b/src/libextra/rl.rs index b7b74694475..59801c945b6 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 593a1ed535d..aeed2d842c1 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 bfa325a21ee..7182f7ff8b7 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 a2f0fd480e1..ffebb87d5cf 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 d1d5b47ff57..9031a001eca 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 640bcc757b3..be170cce07e 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 cbd1e4e7663..58175db1241 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 8551012d6d7..6f89e7ffb07 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 0fd8c5c03d3..17d0d59660f 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 8f86216d240..03b895b3860 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 1a2141e996a..d46e5707f14 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 27cb1c2c100..206d19e175f 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 0b876bf1f81..a1d209d19ac 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 4fbc3862848..b45cde6a8e3 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 8737e571399..01860c3ae99 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 f5b7f34b365..576b1c452a5 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() {} -- cgit 1.4.1-3-g733a5