From 9fd2ac7428afa4f414f32b8b4876ca817ee85f16 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sun, 14 Jul 2013 01:43:31 -0700 Subject: [PATCH] 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 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() {}