1
Fork 0

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
This commit is contained in:
Alex Crichton 2013-07-14 01:43:31 -07:00
parent e3211fa1f1
commit 9fd2ac7428
16 changed files with 81 additions and 70 deletions

View file

@ -69,7 +69,7 @@ pub unsafe fn read(prompt: &str) -> Option<~str> {
pub type CompletionCb = @fn(~str, @fn(~str)); pub type CompletionCb = @fn(~str, @fn(~str));
#[cfg(not(stage0))] #[cfg(not(stage0))]
static complete_key: local_data::Key<@CompletionCb> = &[]; static complete_key: local_data::Key<@CompletionCb> = &local_data::Key;
#[cfg(stage0)] #[cfg(stage0)]
fn complete_key(_: @CompletionCb) {} fn complete_key(_: @CompletionCb) {}

View file

@ -105,6 +105,7 @@ pub mod jit {
use metadata::cstore; use metadata::cstore;
use std::cast; use std::cast;
#[cfg(not(stage0))]
use std::local_data; use std::local_data;
use std::unstable::intrinsics; use std::unstable::intrinsics;
@ -202,18 +203,19 @@ pub mod jit {
// The stage1 compiler won't work, but that doesn't really matter. TLS // The stage1 compiler won't work, but that doesn't really matter. TLS
// changed only very recently to allow storage of owned values. // 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))] #[cfg(not(stage0))]
fn set_engine(engine: ~Engine) { fn set_engine(engine: ~Engine) {
unsafe { local_data::set(engine_key, engine) } local_data::set(engine_key, engine)
} }
#[cfg(stage0)] #[cfg(stage0)]
fn set_engine(_: ~Engine) {} fn set_engine(_: ~Engine) {}
#[cfg(not(stage0))] #[cfg(not(stage0))]
pub fn consume_engine() -> Option<~Engine> { pub fn consume_engine() -> Option<~Engine> {
unsafe { local_data::pop(engine_key) } local_data::pop(engine_key)
} }
#[cfg(stage0)] #[cfg(stage0)]
pub fn consume_engine() -> Option<~Engine> { None } pub fn consume_engine() -> Option<~Engine> { None }

View file

@ -88,7 +88,7 @@ use syntax::abi::{X86, X86_64, Arm, Mips};
pub use middle::trans::context::task_llcx; pub use middle::trans::context::task_llcx;
#[cfg(not(stage0))] #[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)] #[cfg(stage0)]
fn task_local_insn_key(_: @~[&'static str]) {} fn task_local_insn_key(_: @~[&'static str]) {}

View file

@ -239,7 +239,7 @@ impl Drop for CrateContext {
#[cfg(stage0)] #[cfg(stage0)]
fn task_local_llcx_key(_v: @ContextRef) {} fn task_local_llcx_key(_v: @ContextRef) {}
#[cfg(not(stage0))] #[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 { pub fn task_llcx() -> ContextRef {
let opt = 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));

View file

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::cast;
use std::hashmap::HashMap; use std::hashmap::HashMap;
use std::local_data; use std::local_data;
use std::vec;
use syntax::ast; use syntax::ast;
use syntax::parse::token; use syntax::parse::token;
@ -57,7 +57,7 @@ struct LocalVariable {
} }
type LocalCache = @mut HashMap<~str, @~[u8]>; type LocalCache = @mut HashMap<~str, @~[u8]>;
static tls_key: local_data::Key<LocalCache> = &[]; static tls_key: local_data::Key<LocalCache> = &local_data::Key;
impl Program { impl Program {
pub fn new() -> Program { pub fn new() -> Program {
@ -130,16 +130,14 @@ impl Program {
fn main() { 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 // First, get a handle to the tls map which stores all the local
// variables. This works by totally legitimately using the 'code' // variables. This works by totally legitimately using the 'code'
// pointer of the 'tls_key' function as a uint, and then casting it back // pointer of the 'tls_key' function as a uint, and then casting it back
// up to a function // up to a function
code.push_str(fmt!(" code.push_str(fmt!("
let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe { let __tls_map: @mut ::std::hashmap::HashMap<~str, @~[u8]> = unsafe {
let key = ::std::vec::raw::SliceRepr{ data: %? as *u8, let key = ::std::cast::transmute(%u);
len: 0 };
let key = ::std::cast::transmute(key);
::std::local_data::get(key, |k| k.map(|&x| *x)).unwrap() ::std::local_data::get(key, |k| k.map(|&x| *x)).unwrap()
};\n", key as uint)); };\n", key as uint));

View file

@ -13,18 +13,19 @@
Task local data management Task local data management
Allows storing arbitrary types inside task-local-storage (TLS), to be accessed 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. anywhere within a task, keyed by a global pointer parameterized over the type of
Useful for dynamic variables, singletons, and interfacing with foreign code the TLS slot. Useful for dynamic variables, singletons, and interfacing with
with bad callback interfaces. foreign code with bad callback interfaces.
To use, declare a static slice of the type you wish to store. The initialization To use, declare a static variable of the type you wish to store. The
should be `&[]`. This is then the key to what you wish to store. initialization should be `&local_data::Key`. This is then the key to what you
wish to store.
~~~{.rust} ~~~{.rust}
use std::local_data; use std::local_data;
static key_int: local_data::Key<int> = &[]; static key_int: local_data::Key<int> = &local_data::Key;
static key_vector: local_data::Key<~[int]> = &[]; static key_vector: local_data::Key<~[int]> = &local_data::Key;
local_data::set(key_int, 3); local_data::set(key_int, 3);
local_data::get(key_int, |opt| assert_eq!(opt, Some(&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; #[cfg(test)] use task;
/** /**
* Indexes a task-local data slot. The function's code pointer is used for * Indexes a task-local data slot. This pointer is used for comparison to
* comparison. Recommended use is to write an empty function for each desired * differentiate keys from one another. The actual type `T` is not used anywhere
* task-local data slot (and use class destructors, not code inside the * as a member of this type, except that it is parameterized with it to define
* function, if specific teardown is needed). DO NOT use multiple * the type of each key's value.
* instantiations of a single polymorphic function to index data of different
* types; arbitrary type coercion is possible this way.
* *
* One other exception is that this global state can be used in a destructor * The value of each Key is of the singleton enum KeyValue. These also have the
* context to create a circular @-box reference, which will crash during task * same name as `Key` and their purpose is to take up space in the programs data
* failure (see issue #3039). * sections to ensure that each value of the `Key` type points to a unique
* * location.
* These two cases aside, the interface is safe.
*/ */
#[cfg(not(stage0))] #[cfg(not(stage0))]
pub type Key<T> = &'static [T]; pub type Key<T> = &'static KeyValue<T>;
#[cfg(stage0)] #[cfg(stage0)]
pub type Key<'self,T> = &'self fn:Copy(v: T); pub type Key<'self,T> = &'self fn:Copy(v: T);
pub enum KeyValue<T> { Key }
/** /**
* Remove a task-local data value from the table, returning the * Remove a task-local data value from the table, returning the
* reference that was originally created to insert it. * reference that was originally created to insert it.
@ -136,7 +136,7 @@ pub fn modify<T: 'static>(key: Key<T>, f: &fn(Option<T>) -> Option<T>) {
#[test] #[test]
fn test_tls_multitask() { fn test_tls_multitask() {
static my_key: Key<@~str> = &[]; static my_key: Key<@~str> = &Key;
set(my_key, @~"parent data"); set(my_key, @~"parent data");
do task::spawn { do task::spawn {
// TLS shouldn't carry over. // TLS shouldn't carry over.
@ -154,7 +154,7 @@ fn test_tls_multitask() {
#[test] #[test]
fn test_tls_overwrite() { fn test_tls_overwrite() {
static my_key: Key<@~str> = &[]; static my_key: Key<@~str> = &Key;
set(my_key, @~"first data"); set(my_key, @~"first data");
set(my_key, @~"next data"); // Shouldn't leak. set(my_key, @~"next data"); // Shouldn't leak.
assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"next data"); assert!(*(get(my_key, |k| k.map(|&k| *k)).get()) == ~"next data");
@ -162,7 +162,7 @@ fn test_tls_overwrite() {
#[test] #[test]
fn test_tls_pop() { fn test_tls_pop() {
static my_key: Key<@~str> = &[]; static my_key: Key<@~str> = &Key;
set(my_key, @~"weasel"); set(my_key, @~"weasel");
assert!(*(pop(my_key).get()) == ~"weasel"); assert!(*(pop(my_key).get()) == ~"weasel");
// Pop must remove the data from the map. // Pop must remove the data from the map.
@ -171,7 +171,7 @@ fn test_tls_pop() {
#[test] #[test]
fn test_tls_modify() { fn test_tls_modify() {
static my_key: Key<@~str> = &[]; static my_key: Key<@~str> = &Key;
modify(my_key, |data| { modify(my_key, |data| {
match data { match data {
Some(@ref val) => fail!("unwelcome value: %s", *val), 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 // to get recorded as something within a rust stack segment. Then a
// subsequent upcall (esp. for logging, think vsnprintf) would run on // subsequent upcall (esp. for logging, think vsnprintf) would run on
// a stack smaller than 1 MB. // a stack smaller than 1 MB.
static my_key: Key<@~str> = &[]; static my_key: Key<@~str> = &Key;
do task::spawn { do task::spawn {
set(my_key, @~"hax"); set(my_key, @~"hax");
} }
@ -204,9 +204,9 @@ fn test_tls_crust_automorestack_memorial_bug() {
#[test] #[test]
fn test_tls_multiple_types() { fn test_tls_multiple_types() {
static str_key: Key<@~str> = &[]; static str_key: Key<@~str> = &Key;
static box_key: Key<@@()> = &[]; static box_key: Key<@@()> = &Key;
static int_key: Key<@int> = &[]; static int_key: Key<@int> = &Key;
do task::spawn { do task::spawn {
set(str_key, @~"string data"); set(str_key, @~"string data");
set(box_key, @@()); set(box_key, @@());
@ -216,9 +216,9 @@ fn test_tls_multiple_types() {
#[test] #[test]
fn test_tls_overwrite_multiple_types() { fn test_tls_overwrite_multiple_types() {
static str_key: Key<@~str> = &[]; static str_key: Key<@~str> = &Key;
static box_key: Key<@@()> = &[]; static box_key: Key<@@()> = &Key;
static int_key: Key<@int> = &[]; static int_key: Key<@int> = &Key;
do task::spawn { do task::spawn {
set(str_key, @~"string data"); set(str_key, @~"string data");
set(int_key, @42); set(int_key, @42);
@ -233,9 +233,9 @@ fn test_tls_overwrite_multiple_types() {
#[should_fail] #[should_fail]
#[ignore(cfg(windows))] #[ignore(cfg(windows))]
fn test_tls_cleanup_on_failure() { fn test_tls_cleanup_on_failure() {
static str_key: Key<@~str> = &[]; static str_key: Key<@~str> = &Key;
static box_key: Key<@@()> = &[]; static box_key: Key<@@()> = &Key;
static int_key: Key<@int> = &[]; static int_key: Key<@int> = &Key;
set(str_key, @~"parent data"); set(str_key, @~"parent data");
set(box_key, @@()); set(box_key, @@());
do task::spawn { do task::spawn {
@ -252,7 +252,7 @@ fn test_tls_cleanup_on_failure() {
#[test] #[test]
fn test_static_pointer() { fn test_static_pointer() {
static key: Key<@&'static int> = &[]; static key: Key<@&'static int> = &Key;
static VALUE: int = 0; static VALUE: int = 0;
let v: @&'static int = @&VALUE; let v: @&'static int = @&VALUE;
set(key, v); set(key, v);
@ -260,17 +260,17 @@ fn test_static_pointer() {
#[test] #[test]
fn test_owned() { fn test_owned() {
static key: Key<~int> = &[]; static key: Key<~int> = &Key;
set(key, ~1); set(key, ~1);
} }
#[test] #[test]
fn test_same_key_type() { fn test_same_key_type() {
static key1: Key<int> = &[]; static key1: Key<int> = &Key;
static key2: Key<int> = &[]; static key2: Key<int> = &Key;
static key3: Key<int> = &[]; static key3: Key<int> = &Key;
static key4: Key<int> = &[]; static key4: Key<int> = &Key;
static key5: Key<int> = &[]; static key5: Key<int> = &Key;
set(key1, 1); set(key1, 1);
set(key2, 2); set(key2, 2);
set(key3, 3); set(key3, 3);

View file

@ -1242,7 +1242,7 @@ struct OverriddenArgs {
#[cfg(stage0)] #[cfg(stage0)]
fn overridden_arg_key(_v: @OverriddenArgs) {} fn overridden_arg_key(_v: @OverriddenArgs) {}
#[cfg(not(stage0))] #[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 /// Returns the arguments which this program was started with (normally passed
/// via the command line). /// via the command line).

View file

@ -854,7 +854,7 @@ pub fn seed() -> ~[u8] {
#[cfg(stage0)] #[cfg(stage0)]
fn tls_rng_state(_v: @@mut IsaacRng) {} fn tls_rng_state(_v: @@mut IsaacRng) {}
#[cfg(not(stage0))] #[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, * Gives back a lazily initialized task-local random number generator,

View file

@ -348,10 +348,10 @@ mod test {
fn tls() { fn tls() {
use local_data; use local_data;
do run_in_newsched_task() { 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"); local_data::set(key, @~"data");
assert!(*local_data::get(key, |k| k.map(|&k| *k)).get() == ~"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"); local_data::set(key2, @~"data");
assert!(*local_data::get(key2, |k| k.map(|&k| *k)).get() == ~"data"); assert!(*local_data::get(key2, |k| k.map(|&k| *k)).get() == ~"data");
} }

View file

@ -222,6 +222,7 @@ mod std {
pub use condition; pub use condition;
pub use option; pub use option;
pub use kinds; pub use kinds;
pub use local_data;
pub use sys; pub use sys;
pub use pipes; pub use pipes;
pub use unstable; pub use unstable;

View file

@ -17,7 +17,6 @@ use prelude::*;
use ptr; use ptr;
use task::rt; use task::rt;
use util; use util;
use vec;
use super::rt::rust_task; use super::rt::rust_task;
use rt::task::{Task, LocalStorage}; use rt::task::{Task, LocalStorage};
@ -143,7 +142,7 @@ unsafe fn get_local_map(handle: Handle) -> &mut TaskLocalMap {
} }
fn key_to_key_value<T: 'static>(key: local_data::Key<T>) -> *libc::c_void { fn key_to_key_value<T: 'static>(key: local_data::Key<T>) -> *libc::c_void {
return vec::raw::to_ptr(key) as *libc::c_void; unsafe { cast::transmute(key) }
} }
pub unsafe fn local_pop<T: 'static>(handle: Handle, pub unsafe fn local_pop<T: 'static>(handle: Handle,

View file

@ -80,6 +80,7 @@ use cell::Cell;
use container::MutableMap; use container::MutableMap;
use comm::{Chan, GenericChan}; use comm::{Chan, GenericChan};
use hashmap::HashSet; use hashmap::HashSet;
use local_data;
use task::local_data_priv::{local_get, local_set, OldHandle}; use task::local_data_priv::{local_get, local_set, OldHandle};
use task::rt::rust_task; use task::rt::rust_task;
use task::rt; 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 // 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. // a proper closure because the #[test]s won't understand. Have to fake it.
macro_rules! taskgroup_key ( #[cfg(not(stage0))]
// Use a "code pointer" value that will never be a real code pointer. fn taskgroup_key() -> local_data::Key<@@mut TCB> {
() => (cast::transmute((-2 as uint, 0u))) 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) fn gen_child_taskgroup(linked: bool, supervised: bool)
-> (TaskGroupArc, AncestorList, bool) { -> (TaskGroupArc, AncestorList, bool) {
@ -478,7 +483,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
* Step 1. Get spawner's taskgroup info. * Step 1. Get spawner's taskgroup info.
*##################################################################*/ *##################################################################*/
let spawner_group: @@mut TCB = let spawner_group: @@mut TCB =
do local_get(OldHandle(spawner), taskgroup_key!()) |group| { do local_get(OldHandle(spawner), taskgroup_key()) |group| {
match group { match group {
None => { None => {
// Main task, doing first spawn ever. Lazily initialise // Main task, doing first spawn ever. Lazily initialise
@ -495,7 +500,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
AncestorList(None), AncestorList(None),
true, true,
None); None);
local_set(OldHandle(spawner), taskgroup_key!(), group); local_set(OldHandle(spawner), taskgroup_key(), group);
group group
} }
Some(&group) => group Some(&group) => group
@ -688,7 +693,7 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
is_main, is_main,
notifier); notifier);
unsafe { unsafe {
local_set(OldHandle(child), taskgroup_key!(), group); local_set(OldHandle(child), taskgroup_key(), group);
} }
// Run the child's body. // Run the child's body.

View file

@ -695,7 +695,7 @@ pub fn new_sctable_internal() -> SCTable {
// fetch the SCTable from TLS, create one if it doesn't yet exist. // fetch the SCTable from TLS, create one if it doesn't yet exist.
pub fn get_sctable() -> @mut SCTable { pub fn get_sctable() -> @mut SCTable {
#[cfg(not(stage0))] #[cfg(not(stage0))]
static sctable_key: local_data::Key<@@mut SCTable> = &[]; static sctable_key: local_data::Key<@@mut SCTable> = &local_data::Key;
#[cfg(stage0)] #[cfg(stage0)]
fn sctable_key(_: @@mut SCTable) {} fn sctable_key(_: @@mut SCTable) {}
match local_data::get(sctable_key, |k| k.map(|&k| *k)) { match local_data::get(sctable_key, |k| k.map(|&k| *k)) {

View file

@ -580,7 +580,9 @@ pub fn core_macros() -> @str {
pub mod $c { pub mod $c {
#[allow(non_uppercase_statics)]; #[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 : pub static cond :
::std::condition::Condition<$in,$out> = ::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. // FIXME (#6009): remove mod's `pub` below once variant above lands.
pub mod $c { pub mod $c {
#[allow(non_uppercase_statics)]; #[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 : pub static cond :
::std::condition::Condition<$in,$out> = ::std::condition::Condition<$in,$out> =

View file

@ -485,7 +485,8 @@ fn mk_fresh_ident_interner() -> @ident_interner {
// fresh one. // fresh one.
pub fn get_ident_interner() -> @ident_interner { pub fn get_ident_interner() -> @ident_interner {
#[cfg(not(stage0))] #[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)] #[cfg(stage0)]
fn key(_: @@::parse::token::ident_interner) {} fn key(_: @@::parse::token::ident_interner) {}
match local_data::get(key, |k| k.map(|&k| *k)) { match local_data::get(key, |k| k.map(|&k| *k)) {

View file

@ -12,6 +12,7 @@
use std::local_data; 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() {} fn main() {}