// Copyright 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Access to a single thread-local pointer. //! //! The runtime will use this for storing ~Task. //! //! XXX: Add runtime checks for usage of inconsistent pointer types. //! and for overwriting an existing pointer. use libc::c_void; use cast; use ptr; use cell::Cell; use option::{Option, Some, None}; use unstable::finally::Finally; use tls = rt::thread_local_storage; static mut RT_TLS_KEY: tls::Key = -1; /// Initialize the TLS key. Other ops will fail if this isn't executed first. #[fixed_stack_segment] #[inline(never)] pub fn init_tls_key() { unsafe { rust_initialize_rt_tls_key(&mut RT_TLS_KEY); extern { fn rust_initialize_rt_tls_key(key: *mut tls::Key); } } } /// Give a pointer to thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn put(sched: ~T) { let key = tls_key(); let void_ptr: *mut c_void = cast::transmute(sched); tls::set(key, void_ptr); } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. #[inline] pub unsafe fn take() -> ~T { let key = tls_key(); let void_ptr: *mut c_void = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } let ptr: ~T = cast::transmute(void_ptr); tls::set(key, ptr::mut_null()); return ptr; } /// Take ownership of a pointer from thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. /// Leaves the old pointer in TLS for speed. #[inline] pub unsafe fn unsafe_take() -> ~T { let key = tls_key(); let void_ptr: *mut c_void = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } let ptr: ~T = cast::transmute(void_ptr); return ptr; } /// Check whether there is a thread-local pointer installed. pub fn exists() -> bool { unsafe { match maybe_tls_key() { Some(key) => tls::get(key).is_not_null(), None => false } } } /// Borrow the thread-local value from thread-local storage. /// While the value is borrowed it is not available in TLS. /// /// # Safety note /// /// Does not validate the pointer type. pub unsafe fn borrow(f: &fn(&mut T)) { let mut value = take(); // XXX: Need a different abstraction from 'finally' here to avoid unsafety let unsafe_ptr = cast::transmute_mut_region(&mut *value); let value_cell = Cell::new(value); do (|| { f(unsafe_ptr); }).finally { put(value_cell.take()); } } /// Borrow a mutable reference to the thread-local value /// /// # Safety Note /// /// Because this leaves the value in thread-local storage it is possible /// For the Scheduler pointer to be aliased pub unsafe fn unsafe_borrow() -> *mut T { let key = tls_key(); let void_ptr = tls::get(key); if void_ptr.is_null() { rtabort!("thread-local pointer is null. bogus!"); } void_ptr as *mut T } pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { match maybe_tls_key() { Some(key) => { let void_ptr = tls::get(key); if void_ptr.is_null() { None } else { Some(void_ptr as *mut T) } } None => None } } #[inline] fn tls_key() -> tls::Key { match maybe_tls_key() { Some(key) => key, None => rtabort!("runtime tls key not initialized") } } #[inline] #[cfg(not(test))] pub fn maybe_tls_key() -> Option { unsafe { // NB: This is a little racy because, while the key is // initalized under a mutex and it's assumed to be initalized // in the Scheduler ctor by any thread that needs to use it, // we are not accessing the key under a mutex. Threads that // are not using the new Scheduler but still *want to check* // whether they are running under a new Scheduler may see a 0 // value here that is in the process of being initialized in // another thread. I think this is fine since the only action // they could take if it was initialized would be to check the // thread-local value and see that it's not set. if RT_TLS_KEY != -1 { return Some(RT_TLS_KEY); } else { return None; } } } // XXX: The boundary between the running runtime and the testing runtime // seems to be fuzzy at the moment, and trying to use two different keys // results in disaster. This should not be necessary. #[inline] #[cfg(test)] pub fn maybe_tls_key() -> Option { unsafe { ::cast::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } }