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));
#[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) {}

View file

@ -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 }

View file

@ -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]) {}

View file

@ -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));

View file

@ -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<LocalCache> = &[];
static tls_key: local_data::Key<LocalCache> = &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));

View file

@ -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<int> = &[];
static key_vector: local_data::Key<~[int]> = &[];
static key_int: local_data::Key<int> = &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<T> = &'static [T];
pub type Key<T> = &'static KeyValue<T>;
#[cfg(stage0)]
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
* 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]
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<int> = &[];
static key2: Key<int> = &[];
static key3: Key<int> = &[];
static key4: Key<int> = &[];
static key5: Key<int> = &[];
static key1: Key<int> = &Key;
static key2: Key<int> = &Key;
static key3: Key<int> = &Key;
static key4: Key<int> = &Key;
static key5: Key<int> = &Key;
set(key1, 1);
set(key2, 2);
set(key3, 3);

View file

@ -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).

View file

@ -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,

View file

@ -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");
}

View file

@ -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;

View file

@ -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<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,

View file

@ -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.

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.
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)) {

View file

@ -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> =

View file

@ -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)) {

View file

@ -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() {}