// 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; /// 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(); extern { fn rust_initialize_rt_tls_key(); } } } /// Give a pointer to thread-local storage. /// /// # Safety note /// /// Does not validate the pointer type. 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. 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; } /// 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 { match try_unsafe_borrow() { Some(p) => p, None => rtabort!("thread-local pointer is null. bogus!") } } pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { let key = tls_key(); let mut void_ptr: *mut c_void = tls::get(key); if void_ptr.is_null() { return None; } { let ptr: *mut *mut c_void = &mut void_ptr; let ptr: *mut ~T = ptr as *mut ~T; let ptr: *mut T = &mut **ptr; return Some(ptr); } } fn tls_key() -> tls::Key { match maybe_tls_key() { Some(key) => key, None => rtabort!("runtime tls key not initialized") } } #[fixed_stack_segment] #[inline(never)] fn maybe_tls_key() -> Option { unsafe { let key: *mut c_void = rust_get_rt_tls_key(); let key: &mut tls::Key = cast::transmute(key); let key = *key; // Check that the key has been initialized. // 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 key != -1 { return Some(key); } else { return None; } } extern { fn rust_get_rt_tls_key() -> *mut c_void; } }