diff options
Diffstat (limited to 'src/libstd/local_data.rs')
| -rw-r--r-- | src/libstd/local_data.rs | 537 |
1 files changed, 191 insertions, 346 deletions
diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index 9a029de8d6c..c3c04c5470f 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -23,16 +23,14 @@ named and annotated. This name is then passed to the functions in this module to modify/read the slot specified by the key. ```rust -use std::local_data; - local_data_key!(key_int: int) local_data_key!(key_vector: ~[int]) -local_data::set(key_int, 3); -local_data::get(key_int, |opt| assert_eq!(opt.map(|x| *x), Some(3))); +key_int.replace(Some(3)); +assert_eq!(*key_int.get().unwrap(), 3); -local_data::set(key_vector, ~[4]); -local_data::get(key_vector, |opt| assert_eq!(*opt.unwrap(), ~[4])); +key_vector.replace(Some(~[4])); +assert_eq!(*key_vector.get().unwrap(), ~[4]); ``` */ @@ -43,9 +41,12 @@ local_data::get(key_vector, |opt| assert_eq!(*opt.unwrap(), ~[4])); use cast; use iter::{Iterator}; use kinds::Send; +use kinds::marker; use mem::replace; +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; @@ -66,7 +67,7 @@ pub type Key<T> = &'static KeyValue<T>; #[allow(missing_doc)] pub enum KeyValue<T> { Key } -#[allow(missing_doc)] +#[doc(hidden)] trait LocalData {} impl<T: 'static> LocalData for T {} @@ -91,7 +92,7 @@ impl<T: 'static> LocalData for T {} // 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, LoanState)>>; +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. @@ -111,243 +112,156 @@ unsafe fn get_local_map() -> &mut Map { *slot = Some(vec!()); match *slot { Some(ref mut map_ptr) => { return map_ptr } - None => abort() + None => unreachable!(), } } } } -#[deriving(Eq)] -enum LoanState { - NoLoan, ImmLoan, MutLoan -} - -impl LoanState { - fn describe(&self) -> &'static str { - match *self { - NoLoan => "no loan", - ImmLoan => "immutable", - MutLoan => "mutable" - } - } -} - fn key_to_key_value<T: 'static>(key: Key<T>) -> *u8 { - unsafe { cast::transmute(key) } + key as *KeyValue<T> as *u8 } -/// Removes a task-local value from task-local storage. This will return -/// Some(value) if the key was present in TLS, otherwise it will return None. +/// An RAII immutable reference to a task-local value. /// -/// A runtime assertion will be triggered it removal of TLS value is attempted -/// while the value is still loaned out via `get` or `get_mut`. -pub fn pop<T: 'static>(key: Key<T>) -> Option<T> { - let map = unsafe { get_local_map() }; - let key_value = key_to_key_value(key); - - for entry in map.mut_iter() { - match *entry { - Some((k, _, loan)) if k == key_value => { - if loan != NoLoan { - fail!("TLS value cannot be removed because it is currently \ - borrowed as {}", loan.describe()); - } - // Move the data out of the `entry` slot via prelude::replace. - // This is guaranteed to succeed because we already matched - // on `Some` above. - let data = match replace(entry, None) { - Some((_, data, _)) => data, - None => abort() - }; - - // Move `data` into transmute to get out the memory that it - // owns, we must free it manually later. - let (_vtable, alloc): (uint, Box<T>) = unsafe { - cast::transmute(data) - }; - - // Now that we own `alloc`, we can just move out of it as we - // would with any other data. - return Some(*alloc); - } - _ => {} - } - } - return None; +/// 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, } -/// Retrieves a value from TLS. The closure provided is yielded `Some` of a -/// reference to the value located in TLS if one exists, or `None` if the key -/// provided is not present in TLS currently. -/// -/// It is considered a runtime error to attempt to get a value which is already -/// on loan via the `get_mut` method provided. -pub fn get<T: 'static, U>(key: Key<T>, f: |Option<&T>| -> U) -> U { - get_with(key, ImmLoan, f) -} - -/// Retrieves a mutable value from TLS. The closure provided is yielded `Some` -/// of a reference to the mutable value located in TLS if one exists, or `None` -/// if the key provided is not present in TLS currently. -/// -/// It is considered a runtime error to attempt to get a value which is already -/// on loan via this or the `get` methods. -pub fn get_mut<T: 'static, U>(key: Key<T>, f: |Option<&mut T>| -> U) -> U { - get_with(key, MutLoan, |x| { - match x { - None => f(None), - // We're violating a lot of compiler guarantees with this - // invocation of `transmute`, but we're doing runtime checks to - // ensure that it's always valid (only one at a time). - // - // there is no need to be upset! - Some(x) => { f(Some(unsafe { cast::transmute::<&_, &mut _>(x) })) } - } - }) -} +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 { cast::transmute(d) }; + (keyval, d, 0) + }); -fn get_with<T:'static, - U>( - key: Key<T>, - state: LoanState, - f: |Option<&T>| -> U) - -> U { - // This function must be extremely careful. Because TLS can store owned - // values, and we must have some form of `get` function other than `pop`, - // this function has to give a `&` reference back to the caller. - // - // One option is to return the reference, but this cannot be sound because - // the actual lifetime of the object is not known. The slot in TLS could not - // be modified until the object goes out of scope, but the TLS code cannot - // know when this happens. - // - // For this reason, the reference is yielded to a specified closure. This - // way the TLS code knows exactly what the lifetime of the yielded pointer - // is, allowing callers to acquire references to owned data. This is also - // sound so long as measures are taken to ensure that while a TLS slot is - // loaned out to a caller, it's not modified recursively. - let map = unsafe { get_local_map() }; - let key_value = key_to_key_value(key); - - let pos = map.iter().position(|entry| { - match *entry { - Some((k, _, _)) if k == key_value => true, _ => false - } - }); - match pos { - None => { return f(None); } - Some(i) => { - let ret; - let mut return_loan = false; - match *map.get_mut(i) { - Some((_, ref data, ref mut loan)) => { - match (state, *loan) { - (_, NoLoan) => { - *loan = state; - return_loan = true; - } - (ImmLoan, ImmLoan) => {} - (want, cur) => { - fail!("TLS slot cannot be borrowed as {} because \ - it is already borrowed as {}", - want.describe(), cur.describe()); - } - } - // data was created with `box T as Box<LocalData>`, so we - // extract pointer part of the trait, (as Box<T>), and - // then use compiler coercions to achieve a '&' pointer. - unsafe { - match *cast::transmute::<&TLSValue, - &(uint, Box<T>)>(data){ - (_vtable, ref alloc) => { - let value: &T = *alloc; - ret = f(Some(value)); - } - } - } - } - _ => abort() + 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 { cast::transmute(data) }; + let alloc: Box<T> = unsafe { cast::transmute(t.data) }; + + // Now that we own `alloc`, we can just move out of it as we + // would with any other data. + *alloc + }) } - - // n.b. 'data' and 'loans' are both invalid pointers at the point - // 'f' returned because `f` could have appended more TLS items which - // in turn relocated the vector. Hence we do another lookup here to - // fixup the loans. - if return_loan { - match *map.get_mut(i) { - Some((_, _, ref mut loan)) => { *loan = NoLoan; } - None => abort() - } + None => { + map.push(newval); + None } - return ret; } } -} -fn abort() -> ! { - use intrinsics; - unsafe { intrinsics::abort() } -} + /// 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 } + }) + } -/// Inserts a value into task local storage. If the key is already present in -/// TLS, then the previous value is removed and replaced with the provided data. -/// -/// It is considered a runtime error to attempt to set a key which is currently -/// on loan via the `get` or `get_mut` methods. -pub fn set<T: 'static>(key: Key<T>, data: T) { - let map = unsafe { get_local_map() }; - let keyval = key_to_key_value(key); - - // 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 'Box<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. - let data = box data as Box<LocalData:>; - - fn insertion_position(map: &mut Map, - key: *u8) -> Option<uint> { - // First see if the map contains this key already - let curspot = map.iter().position(|entry| { + 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((ekey, _, loan)) if key == ekey => { - if loan != NoLoan { - fail!("TLS value cannot be overwritten because it is - already borrowed as {}", loan.describe()) - } - true + Some((k, ref data, ref mut loan)) if k == key_value => { + Some((i, data, loan)) } - _ => false, + _ => None } - }); - // If it doesn't contain the key, just find a slot that's None - match curspot { - Some(i) => Some(i), - None => map.iter().position(|entry| entry.is_none()) - } + }).next() } +} - // 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 data: Box<LocalData:Send> = unsafe { cast::transmute(data) }; - match insertion_position(map, keyval) { - Some(i) => { *map.get_mut(i) = Some((keyval, data, NoLoan)); } - None => { map.push(Some((keyval, data, NoLoan))); } - } +impl<T: 'static> Deref<T> for Ref<T> { + fn deref<'a>(&'a self) -> &'a T { self.ptr } } -/// Modifies a task-local value by temporarily removing it from task-local -/// storage and then re-inserting if `Some` is returned from the closure. -/// -/// This function will have the same runtime errors as generated from `pop` and -/// `set` (the key must not currently be on loan -pub fn modify<T: 'static>(key: Key<T>, f: |Option<T>| -> Option<T>) { - match f(pop(key)) { - Some(next) => { set(key, next); } - None => {} +#[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; } } @@ -362,55 +276,36 @@ mod tests { #[test] fn test_tls_multitask() { static my_key: Key<~str> = &Key; - set(my_key, "parent data".to_owned()); + my_key.replace(Some("parent data".to_owned())); task::spawn(proc() { // TLS shouldn't carry over. - assert!(get(my_key, |k| k.map(|k| (*k).clone())).is_none()); - set(my_key, "child data".to_owned()); - assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == - "child data".to_owned()); + 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!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned()); - assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned()); - assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "parent data".to_owned()); + 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; - set(my_key, "first data".to_owned()); - set(my_key, "next data".to_owned()); // Shouldn't leak. - assert!(get(my_key, |k| k.map(|k| (*k).clone())).unwrap() == "next data".to_owned()); + 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; - set(my_key, "weasel".to_owned()); - assert!(pop(my_key).unwrap() == "weasel".to_owned()); + 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!(pop(my_key).is_none()); - } - - #[test] - fn test_tls_modify() { - static my_key: Key<~str> = &Key; - modify(my_key, |data| { - match data { - Some(ref val) => fail!("unwelcome value: {}", *val), - None => Some("first data".to_owned()) - } - }); - modify(my_key, |data| { - match data.as_ref().map(|s| s.as_slice()) { - Some("first data") => Some("next data".to_owned()), - Some(ref val) => fail!("wrong value: {}", *val), - None => fail!("missing value") - } - }); - assert!(pop(my_key).unwrap() == "next data".to_owned()); + assert!(my_key.replace(None).is_none()); } #[test] @@ -423,7 +318,7 @@ mod tests { // a stack smaller than 1 MB. static my_key: Key<~str> = &Key; task::spawn(proc() { - set(my_key, "hax".to_owned()); + my_key.replace(Some("hax".to_owned())); }); } @@ -433,28 +328,27 @@ mod tests { static box_key: Key<@()> = &Key; static int_key: Key<int> = &Key; task::spawn(proc() { - set(str_key, "string data".to_owned()); - set(box_key, @()); - set(int_key, 42); + str_key.replace(Some("string data".to_owned())); + box_key.replace(Some(@())); + int_key.replace(Some(42)); }); } #[test] - #[allow(dead_code)] 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() { - set(str_key, "string data".to_owned()); - set(str_key, "string data 2".to_owned()); - set(box_key, @()); - set(box_key, @()); - set(int_key, 42); + 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. - set(int_key, 31337); + int_key.replace(Some(31337)); }); } @@ -464,17 +358,16 @@ mod tests { static str_key: Key<~str> = &Key; static box_key: Key<@()> = &Key; static int_key: Key<int> = &Key; - set(str_key, "parent data".to_owned()); - set(box_key, @()); + str_key.replace(Some("parent data".to_owned())); + box_key.replace(Some(@())); task::spawn(proc() { - // spawn_linked - set(str_key, "string data".to_owned()); - set(box_key, @()); - set(int_key, 42); + str_key.replace(Some("string data".to_owned())); + box_key.replace(Some(@())); + int_key.replace(Some(42)); fail!(); }); // Not quite nondeterministic. - set(int_key, 31337); + int_key.replace(Some(31337)); fail!(); } @@ -482,42 +375,24 @@ mod tests { fn test_static_pointer() { static key: Key<&'static int> = &Key; static VALUE: int = 0; - let v: &'static int = &VALUE; - set(key, v); + key.replace(Some(&VALUE)); } #[test] fn test_owned() { static key: Key<Box<int>> = &Key; - set(key, box 1); - - get(key, |v| { - get(key, |v| { - get(key, |v| { - assert_eq!(**v.unwrap(), 1); - }); - assert_eq!(**v.unwrap(), 1); - }); - assert_eq!(**v.unwrap(), 1); - }); - set(key, box 2); - get(key, |v| { - assert_eq!(**v.unwrap(), 2); - }) - } - - #[test] - fn test_get_mut() { - static key: Key<int> = &Key; - set(key, 1); - - get_mut(key, |v| { - *v.unwrap() = 2; - }); - - get(key, |v| { - assert_eq!(*v.unwrap(), 2); - }) + 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] @@ -527,56 +402,26 @@ mod tests { static key3: Key<int> = &Key; static key4: Key<int> = &Key; static key5: Key<int> = &Key; - set(key1, 1); - set(key2, 2); - set(key3, 3); - set(key4, 4); - set(key5, 5); - - get(key1, |x| assert_eq!(*x.unwrap(), 1)); - get(key2, |x| assert_eq!(*x.unwrap(), 2)); - get(key3, |x| assert_eq!(*x.unwrap(), 3)); - get(key4, |x| assert_eq!(*x.unwrap(), 4)); - get(key5, |x| assert_eq!(*x.unwrap(), 5)); + 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; - set(key, 4); - get(key, |_| { - set(key, 4); - }) - } - - #[test] - #[should_fail] - fn test_nested_get_mut2() { - static key: Key<int> = &Key; - set(key, 4); - get(key, |_| { - get_mut(key, |_| {}) - }) - } - - #[test] - #[should_fail] - fn test_nested_get_mut3() { - static key: Key<int> = &Key; - set(key, 4); - get_mut(key, |_| { - get(key, |_| {}) - }) - } + key.replace(Some(4)); - #[test] - #[should_fail] - fn test_nested_get_mut4() { - static key: Key<int> = &Key; - set(key, 4); - get_mut(key, |_| { - get_mut(key, |_| {}) - }) + let _k = key.get(); + key.replace(Some(4)); } } |
