2013-05-30 05:16:33 -05:00
|
|
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
2012-12-03 18:48:01 -06:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2012-09-19 19:29:54 -05:00
|
|
|
/*!
|
|
|
|
|
|
|
|
Task local data management
|
|
|
|
|
2013-07-09 19:25:28 -05:00
|
|
|
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.
|
2012-09-19 19:29:54 -05:00
|
|
|
|
2013-07-09 19:25:28 -05:00
|
|
|
To use, declare a monomorphic (no type parameters) global function at the type
|
|
|
|
to store, and use it as the 'key' when accessing.
|
|
|
|
|
|
|
|
~~~{.rust}
|
|
|
|
use std::local_data;
|
|
|
|
|
|
|
|
fn key_int(_: @int) {}
|
|
|
|
fn key_vector(_: @~[int]) {}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
local_data::set(key_int, @3);
|
|
|
|
assert!(local_data::get(key_int) == Some(@3));
|
|
|
|
|
|
|
|
local_data::set(key_vector, @~[3]);
|
|
|
|
assert!(local_data::get(key_vector).unwrap()[0] == 3);
|
|
|
|
}
|
|
|
|
~~~
|
2012-09-19 19:29:54 -05:00
|
|
|
|
2012-09-19 19:43:41 -05:00
|
|
|
Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation
|
|
|
|
magic.
|
2012-09-19 19:29:54 -05:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2013-01-08 21:37:25 -06:00
|
|
|
use prelude::*;
|
2013-05-24 21:35:29 -05:00
|
|
|
|
2013-07-09 11:40:50 -05:00
|
|
|
use task::local_data_priv::{local_get, local_pop, local_set, Handle};
|
2013-05-30 05:16:33 -05:00
|
|
|
|
|
|
|
#[cfg(test)] use task;
|
2012-09-19 19:29:54 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2013-07-09 19:25:28 -05:00
|
|
|
pub type Key<'self,T> = &'self fn:Copy(v: T);
|
2012-09-19 19:29:54 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a task-local data value from the table, returning the
|
|
|
|
* reference that was originally created to insert it.
|
|
|
|
*/
|
2013-07-11 00:14:40 -05:00
|
|
|
#[cfg(stage0)]
|
|
|
|
pub unsafe fn pop<T: 'static>(key: Key<@T>) -> Option<@T> {
|
|
|
|
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))]
|
2013-07-09 19:25:28 -05:00
|
|
|
pub unsafe fn pop<T: 'static>(key: Key<T>) -> Option<T> {
|
2013-04-22 14:54:03 -05:00
|
|
|
local_pop(Handle::new(), key)
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Retrieve a task-local data value. It will also be kept alive in the
|
|
|
|
* table until explicitly removed.
|
|
|
|
*/
|
2013-07-11 00:14:40 -05:00
|
|
|
#[cfg(stage0)]
|
|
|
|
pub unsafe fn get<T: 'static>(key: Key<@T>) -> Option<@T> {
|
|
|
|
local_get(Handle::new(), key, |loc| loc.map(|&x| *x))
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Retrieve a task-local data value. It will also be kept alive in the
|
|
|
|
* table until explicitly removed.
|
|
|
|
*/
|
|
|
|
#[cfg(not(stage0))]
|
2013-07-09 19:25:28 -05:00
|
|
|
pub unsafe fn get<T: 'static>(key: Key<@T>) -> Option<@T> {
|
2013-07-09 16:52:01 -05:00
|
|
|
local_get(Handle::new(), key, |loc| loc.map(|&x| *x))
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Store a value in task-local data. If this key already has a value,
|
|
|
|
* that value is overwritten (and its destructor is run).
|
|
|
|
*/
|
2013-07-11 00:14:40 -05:00
|
|
|
#[cfg(stage0)]
|
2013-07-09 19:25:28 -05:00
|
|
|
pub unsafe fn set<T: 'static>(key: Key<@T>, data: @T) {
|
2013-04-22 14:54:03 -05:00
|
|
|
local_set(Handle::new(), key, data)
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
2013-07-11 00:14:40 -05:00
|
|
|
/**
|
|
|
|
* 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<T: 'static>(key: Key<T>, data: T) {
|
|
|
|
local_set(Handle::new(), key, data)
|
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
/**
|
|
|
|
* Modify a task-local data value. If the function returns 'None', the
|
|
|
|
* data is removed (and its reference dropped).
|
|
|
|
*/
|
2013-07-11 00:14:40 -05:00
|
|
|
#[cfg(stage0)]
|
2013-07-09 19:25:28 -05:00
|
|
|
pub unsafe fn modify<T: 'static>(key: Key<@T>,
|
|
|
|
f: &fn(Option<@T>) -> Option<@T>) {
|
|
|
|
match f(pop(key)) {
|
|
|
|
Some(next) => { set(key, next); }
|
2013-07-09 11:40:50 -05:00
|
|
|
None => {}
|
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
2013-07-11 00:14:40 -05:00
|
|
|
/**
|
|
|
|
* Modify a task-local data value. If the function returns 'None', the
|
|
|
|
* data is removed (and its reference dropped).
|
|
|
|
*/
|
|
|
|
#[cfg(not(stage0))]
|
|
|
|
pub unsafe fn modify<T: 'static>(key: Key<T>,
|
|
|
|
f: &fn(Option<T>) -> Option<T>) {
|
|
|
|
match f(pop(key)) {
|
|
|
|
Some(next) => { set(key, next); }
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_multitask() {
|
|
|
|
unsafe {
|
|
|
|
fn my_key(_x: @~str) { }
|
2013-07-09 19:25:28 -05:00
|
|
|
set(my_key, @~"parent data");
|
2013-01-23 13:43:58 -06:00
|
|
|
do task::spawn {
|
2013-06-27 10:45:24 -05:00
|
|
|
// TLS shouldn't carry over.
|
2013-07-09 19:25:28 -05:00
|
|
|
assert!(get(my_key).is_none());
|
|
|
|
set(my_key, @~"child data");
|
|
|
|
assert!(*(get(my_key).get()) ==
|
2013-03-06 21:09:17 -06:00
|
|
|
~"child data");
|
2013-06-27 10:45:24 -05:00
|
|
|
// should be cleaned up for us
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
|
|
|
// Must work multiple times
|
2013-07-09 19:25:28 -05:00
|
|
|
assert!(*(get(my_key).get()) == ~"parent data");
|
|
|
|
assert!(*(get(my_key).get()) == ~"parent data");
|
|
|
|
assert!(*(get(my_key).get()) == ~"parent data");
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_overwrite() {
|
|
|
|
unsafe {
|
|
|
|
fn my_key(_x: @~str) { }
|
2013-07-09 19:25:28 -05:00
|
|
|
set(my_key, @~"first data");
|
|
|
|
set(my_key, @~"next data"); // Shouldn't leak.
|
|
|
|
assert!(*(get(my_key).get()) == ~"next data");
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_pop() {
|
|
|
|
unsafe {
|
|
|
|
fn my_key(_x: @~str) { }
|
2013-07-09 19:25:28 -05:00
|
|
|
set(my_key, @~"weasel");
|
|
|
|
assert!(*(pop(my_key).get()) == ~"weasel");
|
2013-01-23 13:43:58 -06:00
|
|
|
// Pop must remove the data from the map.
|
2013-07-09 19:25:28 -05:00
|
|
|
assert!(pop(my_key).is_none());
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_modify() {
|
|
|
|
unsafe {
|
|
|
|
fn my_key(_x: @~str) { }
|
2013-07-09 19:25:28 -05:00
|
|
|
modify(my_key, |data| {
|
2013-01-23 13:43:58 -06:00
|
|
|
match data {
|
2013-05-05 17:18:51 -05:00
|
|
|
Some(@ref val) => fail!("unwelcome value: %s", *val),
|
2013-05-09 06:52:07 -05:00
|
|
|
None => Some(@~"first data")
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
|
|
|
});
|
2013-07-09 19:25:28 -05:00
|
|
|
modify(my_key, |data| {
|
2013-01-23 13:43:58 -06:00
|
|
|
match data {
|
|
|
|
Some(@~"first data") => Some(@~"next data"),
|
2013-05-09 06:52:07 -05:00
|
|
|
Some(@ref val) => fail!("wrong value: %s", *val),
|
2013-05-05 17:18:51 -05:00
|
|
|
None => fail!("missing value")
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
|
|
|
});
|
2013-07-09 19:25:28 -05:00
|
|
|
assert!(*(pop(my_key).get()) == ~"next data");
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_crust_automorestack_memorial_bug() {
|
2013-04-18 19:40:37 -05:00
|
|
|
// This might result in a stack-canary clobber if the runtime fails to
|
|
|
|
// set sp_limit to 0 when calling the cleanup extern - it might
|
|
|
|
// automatically jump over to the rust stack, which causes next_c_sp
|
|
|
|
// 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) { }
|
|
|
|
do task::spawn {
|
2013-07-09 19:25:28 -05:00
|
|
|
unsafe { set(my_key, @~"hax"); }
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_multiple_types() {
|
2013-04-18 19:40:37 -05:00
|
|
|
fn str_key(_x: @~str) { }
|
|
|
|
fn box_key(_x: @@()) { }
|
|
|
|
fn int_key(_x: @int) { }
|
|
|
|
do task::spawn {
|
|
|
|
unsafe {
|
2013-07-09 19:25:28 -05:00
|
|
|
set(str_key, @~"string data");
|
|
|
|
set(box_key, @@());
|
|
|
|
set(int_key, @42);
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2012-10-04 00:06:51 -05:00
|
|
|
fn test_tls_overwrite_multiple_types() {
|
2012-10-02 13:37:37 -05:00
|
|
|
fn str_key(_x: @~str) { }
|
|
|
|
fn box_key(_x: @@()) { }
|
|
|
|
fn int_key(_x: @int) { }
|
2013-01-23 13:43:58 -06:00
|
|
|
do task::spawn {
|
|
|
|
unsafe {
|
2013-07-09 19:25:28 -05:00
|
|
|
set(str_key, @~"string data");
|
|
|
|
set(int_key, @42);
|
2013-01-23 13:43:58 -06:00
|
|
|
// This could cause a segfault if overwriting-destruction is done
|
|
|
|
// with the crazy polymorphic transmute rather than the provided
|
|
|
|
// finaliser.
|
2013-07-09 19:25:28 -05:00
|
|
|
set(int_key, @31337);
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[should_fail]
|
|
|
|
#[ignore(cfg(windows))]
|
2013-01-23 13:43:58 -06:00
|
|
|
fn test_tls_cleanup_on_failure() {
|
|
|
|
unsafe {
|
|
|
|
fn str_key(_x: @~str) { }
|
|
|
|
fn box_key(_x: @@()) { }
|
|
|
|
fn int_key(_x: @int) { }
|
2013-07-09 19:25:28 -05:00
|
|
|
set(str_key, @~"parent data");
|
|
|
|
set(box_key, @@());
|
2013-01-23 13:43:58 -06:00
|
|
|
do task::spawn {
|
2013-06-27 10:45:24 -05:00
|
|
|
// spawn_linked
|
2013-07-09 19:25:28 -05:00
|
|
|
set(str_key, @~"string data");
|
|
|
|
set(box_key, @@());
|
|
|
|
set(int_key, @42);
|
2013-06-27 10:45:24 -05:00
|
|
|
fail!();
|
2013-01-23 13:43:58 -06:00
|
|
|
}
|
|
|
|
// Not quite nondeterministic.
|
2013-07-09 19:25:28 -05:00
|
|
|
set(int_key, @31337);
|
2013-02-11 21:26:38 -06:00
|
|
|
fail!();
|
2012-09-19 19:29:54 -05:00
|
|
|
}
|
|
|
|
}
|
2013-05-07 15:53:25 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_static_pointer() {
|
|
|
|
unsafe {
|
|
|
|
fn key(_x: @&'static int) { }
|
|
|
|
static VALUE: int = 0;
|
2013-07-09 19:25:28 -05:00
|
|
|
set(key, @&VALUE);
|
2013-05-07 15:53:25 -05:00
|
|
|
}
|
2013-05-05 17:18:51 -05:00
|
|
|
}
|
2013-07-11 02:19:23 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_owned() {
|
|
|
|
unsafe {
|
|
|
|
fn key(_x: ~int) { }
|
|
|
|
set(key, ~1);
|
|
|
|
}
|
|
|
|
}
|