rust/src/libstd/local_data.rs
Alex Crichton f94d671bfa core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.

* transmute - This function was moved to `mem`, but it is now marked as
              #[unstable]. This is due to planned changes to the `transmute`
              function and how it can be invoked (see the #[unstable] comment).
              For more information, see RFC 5 and #12898

* transmute_copy - This function was moved to `mem`, with clarification that is
                   is not an error to invoke it with T/U that are different
                   sizes, but rather that it is strongly discouraged. This
                   function is now #[stable]

* forget - This function was moved to `mem` and marked #[stable]

* bump_box_refcount - This function was removed due to the deprecation of
                      managed boxes as well as its questionable utility.

* transmute_mut - This function was previously deprecated, and removed as part
                  of this commit.

* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
                         can be achieved with an `as` in safe code, so it was
                         removed.

* transmute_lifetime - This function was removed because it is likely a strong
                       indication that code is incorrect in the first place.

* transmute_mut_lifetime - This function was removed for the same reasons as
                           `transmute_lifetime`

* copy_lifetime - This function was moved to `mem`, but it is marked
                  `#[unstable]` now due to the likelihood of being removed in
                  the future if it is found to not be very useful.

* copy_mut_lifetime - This function was also moved to `mem`, but had the same
                      treatment as `copy_lifetime`.

* copy_lifetime_vec - This function was removed because it is not used today,
                      and its existence is not necessary with DST
                      (copy_lifetime will suffice).

In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.

    transmute - #[unstable]
    transmute_copy - #[stable]
    forget - #[stable]
    copy_lifetime - #[unstable]
    copy_mut_lifetime - #[unstable]

[breaking-change]
2014-05-11 01:13:02 -07:00

427 lines
14 KiB
Rust

// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// 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.
/*!
Task local data management
Allows storing arbitrary types inside task-local-storage (TLS), to be accessed
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 declare a new key for storing local data of a particular type, use the
`local_data_key!` macro. This macro will expand to a `static` item appropriately
named and annotated. This name is then passed to the functions in this module to
modify/read the slot specified by the key.
```rust
local_data_key!(key_int: int)
local_data_key!(key_vector: ~[int])
key_int.replace(Some(3));
assert_eq!(*key_int.get().unwrap(), 3);
key_vector.replace(Some(~[4]));
assert_eq!(*key_vector.get().unwrap(), ~[4]);
```
*/
// Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation
// magic.
use iter::{Iterator};
use kinds::Send;
use kinds::marker;
use mem::replace;
use mem;
use ops::{Drop, Deref};
use option::{None, Option, Some};
use owned::Box;
use raw;
use rt::task::{Task, LocalStorage};
use slice::{ImmutableVector, MutableVector};
use vec::Vec;
/**
* 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.
*
* 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.
*/
pub type Key<T> = &'static KeyValue<T>;
#[allow(missing_doc)]
pub enum KeyValue<T> { Key }
#[doc(hidden)]
trait LocalData {}
impl<T: 'static> LocalData for T {}
// The task-local-map stores all TLS information for the currently running task.
// It is stored as an owned pointer into the runtime, and it's only allocated
// when TLS is used for the first time. This map must be very carefully
// constructed because it has many mutable loans unsoundly handed out on it to
// the various invocations of TLS requests.
//
// One of the most important operations is loaning a value via `get` to a
// caller. In doing so, the slot that the TLS entry is occupying cannot be
// invalidated because upon returning its loan state must be updated. Currently
// the TLS map is a vector, but this is possibly dangerous because the vector
// can be reallocated/moved when new values are pushed onto it.
//
// This problem currently isn't solved in a very elegant way. Inside the `get`
// function, it internally "invalidates" all references after the loan is
// finished and looks up into the vector again. In theory this will prevent
// pointers from being moved under our feet so long as LLVM doesn't go too crazy
// with the optimizations.
//
// n.b. If TLS is used heavily in future, this could be made more efficient with
// a proper map.
#[doc(hidden)]
pub type Map = Vec<Option<(*u8, TLSValue, uint)>>;
type TLSValue = Box<LocalData:Send>;
// Gets the map from the runtime. Lazily initialises if not done so already.
unsafe fn get_local_map() -> &mut Map {
use rt::local::Local;
let task: *mut Task = Local::unsafe_borrow();
match &mut (*task).storage {
// If the at_exit function is already set, then we just need to take
// a loan out on the TLS map stored inside
&LocalStorage(Some(ref mut map_ptr)) => {
return map_ptr;
}
// If this is the first time we've accessed TLS, perform similar
// actions to the oldsched way of doing things.
&LocalStorage(ref mut slot) => {
*slot = Some(vec!());
match *slot {
Some(ref mut map_ptr) => { return map_ptr }
None => unreachable!(),
}
}
}
}
fn key_to_key_value<T: 'static>(key: Key<T>) -> *u8 {
key as *KeyValue<T> as *u8
}
/// An RAII immutable reference to a task-local value.
///
/// The task-local data can be accessed through this value, and when this
/// structure is dropped it will return the borrow on the data.
pub struct Ref<T> {
ptr: &'static T,
key: Key<T>,
index: uint,
nosend: marker::NoSend,
}
impl<T: 'static> KeyValue<T> {
/// Replaces a value in task local storage.
///
/// If this key is already present in TLS, then the previous value is
/// replaced with the provided data, and then returned.
///
/// # Failure
///
/// This function will fail if this key is present in TLS and currently on
/// loan with the `get` method.
///
/// # Example
///
/// ```
/// local_data_key!(foo: int)
///
/// assert_eq!(foo.replace(Some(10)), None);
/// assert_eq!(foo.replace(Some(4)), Some(10));
/// assert_eq!(foo.replace(None), Some(4));
/// ```
pub fn replace(&'static self, data: Option<T>) -> Option<T> {
let map = unsafe { get_local_map() };
let keyval = key_to_key_value(self);
// When the task-local map is destroyed, all the data needs to be
// cleaned up. For this reason we can't do some clever tricks to store
// '~T' as a '*c_void' or something like that. To solve the problem, we
// cast everything to a trait (LocalData) which is then stored inside
// the map. Upon destruction of the map, all the objects will be
// destroyed and the traits have enough information about them to
// destroy themselves.
//
// Additionally, the type of the local data map must ascribe to Send, so
// we do the transmute here to add the Send bound back on. This doesn't
// actually matter because TLS will always own the data (until its moved
// out) and we're not actually sending it to other schedulers or
// anything.
let newval = data.map(|d| {
let d = box d as Box<LocalData>;
let d: Box<LocalData:Send> = unsafe { mem::transmute(d) };
(keyval, d, 0)
});
let pos = match self.find(map) {
Some((i, _, &0)) => Some(i),
Some((_, _, _)) => fail!("TLS value cannot be replaced because it \
is already borrowed"),
None => map.iter().position(|entry| entry.is_none()),
};
match pos {
Some(i) => {
replace(map.get_mut(i), newval).map(|(_, data, _)| {
// Move `data` into transmute to get out the memory that it
// owns, we must free it manually later.
let t: raw::TraitObject = unsafe { mem::transmute(data) };
let alloc: Box<T> = unsafe { mem::transmute(t.data) };
// Now that we own `alloc`, we can just move out of it as we
// would with any other data.
*alloc
})
}
None => {
map.push(newval);
None
}
}
}
/// Borrows a value from TLS.
///
/// If `None` is returned, then this key is not present in TLS. If `Some` is
/// returned, then the returned data is a smart pointer representing a new
/// loan on this TLS key. While on loan, this key cannot be altered via the
/// `replace` method.
///
/// # Example
///
/// ```
/// local_data_key!(key: int)
///
/// assert!(key.get().is_none());
///
/// key.replace(Some(3));
/// assert_eq!(*key.get().unwrap(), 3);
/// ```
pub fn get(&'static self) -> Option<Ref<T>> {
let map = unsafe { get_local_map() };
self.find(map).map(|(pos, data, loan)| {
*loan += 1;
// data was created with `~T as ~LocalData`, so we extract
// pointer part of the trait, (as ~T), and then use
// compiler coercions to achieve a '&' pointer.
let ptr = unsafe {
let data = data as *Box<LocalData:Send> as *raw::TraitObject;
&mut *((*data).data as *mut T)
};
Ref { ptr: ptr, index: pos, nosend: marker::NoSend, key: self }
})
}
fn find<'a>(&'static self,
map: &'a mut Map) -> Option<(uint, &'a TLSValue, &'a mut uint)>{
let key_value = key_to_key_value(self);
map.mut_iter().enumerate().filter_map(|(i, entry)| {
match *entry {
Some((k, ref data, ref mut loan)) if k == key_value => {
Some((i, data, loan))
}
_ => None
}
}).next()
}
}
impl<T: 'static> Deref<T> for Ref<T> {
fn deref<'a>(&'a self) -> &'a T { self.ptr }
}
#[unsafe_destructor]
impl<T: 'static> Drop for Ref<T> {
fn drop(&mut self) {
let map = unsafe { get_local_map() };
let (_, _, ref mut loan) = *map.get_mut(self.index).get_mut_ref();
*loan -= 1;
}
}
#[cfg(test)]
mod tests {
use prelude::*;
use super::*;
use owned::Box;
use task;
#[test]
fn test_tls_multitask() {
static my_key: Key<~str> = &Key;
my_key.replace(Some("parent data".to_owned()));
task::spawn(proc() {
// TLS shouldn't carry over.
assert!(my_key.get().is_none());
my_key.replace(Some("child data".to_owned()));
assert!(my_key.get().get_ref().as_slice() == "child data");
// should be cleaned up for us
});
// Must work multiple times
assert!(my_key.get().unwrap().as_slice() == "parent data");
assert!(my_key.get().unwrap().as_slice() == "parent data");
assert!(my_key.get().unwrap().as_slice() == "parent data");
}
#[test]
fn test_tls_overwrite() {
static my_key: Key<~str> = &Key;
my_key.replace(Some("first data".to_owned()));
my_key.replace(Some("next data".to_owned())); // Shouldn't leak.
assert!(my_key.get().unwrap().as_slice() == "next data");
}
#[test]
fn test_tls_pop() {
static my_key: Key<~str> = &Key;
my_key.replace(Some("weasel".to_owned()));
assert!(my_key.replace(None).unwrap() == "weasel".to_owned());
// Pop must remove the data from the map.
assert!(my_key.replace(None).is_none());
}
#[test]
fn test_tls_crust_automorestack_memorial_bug() {
// 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.
static my_key: Key<~str> = &Key;
task::spawn(proc() {
my_key.replace(Some("hax".to_owned()));
});
}
#[test]
fn test_tls_multiple_types() {
static str_key: Key<~str> = &Key;
static box_key: Key<@()> = &Key;
static int_key: Key<int> = &Key;
task::spawn(proc() {
str_key.replace(Some("string data".to_owned()));
box_key.replace(Some(@()));
int_key.replace(Some(42));
});
}
#[test]
fn test_tls_overwrite_multiple_types() {
static str_key: Key<~str> = &Key;
static box_key: Key<@()> = &Key;
static int_key: Key<int> = &Key;
task::spawn(proc() {
str_key.replace(Some("string data".to_owned()));
str_key.replace(Some("string data 2".to_owned()));
box_key.replace(Some(@()));
box_key.replace(Some(@()));
int_key.replace(Some(42));
// This could cause a segfault if overwriting-destruction is done
// with the crazy polymorphic transmute rather than the provided
// finaliser.
int_key.replace(Some(31337));
});
}
#[test]
#[should_fail]
fn test_tls_cleanup_on_failure() {
static str_key: Key<~str> = &Key;
static box_key: Key<@()> = &Key;
static int_key: Key<int> = &Key;
str_key.replace(Some("parent data".to_owned()));
box_key.replace(Some(@()));
task::spawn(proc() {
str_key.replace(Some("string data".to_owned()));
box_key.replace(Some(@()));
int_key.replace(Some(42));
fail!();
});
// Not quite nondeterministic.
int_key.replace(Some(31337));
fail!();
}
#[test]
fn test_static_pointer() {
static key: Key<&'static int> = &Key;
static VALUE: int = 0;
key.replace(Some(&VALUE));
}
#[test]
fn test_owned() {
static key: Key<Box<int>> = &Key;
key.replace(Some(box 1));
{
let k1 = key.get().unwrap();
let k2 = key.get().unwrap();
let k3 = key.get().unwrap();
assert_eq!(**k1, 1);
assert_eq!(**k2, 1);
assert_eq!(**k3, 1);
}
key.replace(Some(box 2));
assert_eq!(**key.get().unwrap(), 2);
}
#[test]
fn test_same_key_type() {
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;
key1.replace(Some(1));
key2.replace(Some(2));
key3.replace(Some(3));
key4.replace(Some(4));
key5.replace(Some(5));
assert_eq!(*key1.get().unwrap(), 1);
assert_eq!(*key2.get().unwrap(), 2);
assert_eq!(*key3.get().unwrap(), 3);
assert_eq!(*key4.get().unwrap(), 4);
assert_eq!(*key5.get().unwrap(), 5);
}
#[test]
#[should_fail]
fn test_nested_get_set1() {
static key: Key<int> = &Key;
key.replace(Some(4));
let _k = key.get();
key.replace(Some(4));
}
}