pub unsafe fn init(argc: int, argv: **u8) { realargs::init(argc, argv) }
/// One-time global cleanup.
-#[cfg(not(test))] pub fn cleanup() { imp::cleanup() }
-#[cfg(test)] pub fn cleanup() { realargs::cleanup() }
+#[cfg(not(test))] pub unsafe fn cleanup() { imp::cleanup() }
+#[cfg(test)] pub unsafe fn cleanup() { realargs::cleanup() }
/// Take the global arguments from global storage.
#[cfg(not(test))] pub fn take() -> Option<~[~str]> { imp::take() }
use vec;
static mut global_args_ptr: uint = 0;
+ static mut lock: Mutex = MUTEX_INIT;
pub unsafe fn init(argc: int, argv: **u8) {
let args = load_argc_and_argv(argc, argv);
put(args);
}
- pub fn cleanup() {
+ pub unsafe fn cleanup() {
rtassert!(take().is_some());
+ lock.destroy();
}
pub fn take() -> Option<~[~str]> {
}
fn with_lock<T>(f: || -> T) -> T {
- static mut lock: Mutex = MUTEX_INIT;
(|| {
unsafe {
lock.lock();
#[cfg(stage0)]
#[cfg(windows)]
static mut RT_TLS_KEY: tls::Key = -1;
+static mut tls_lock: Mutex = MUTEX_INIT;
+static mut tls_initialized: bool = false;
/// Initialize the TLS key. Other ops will fail if this isn't executed first.
#[inline(never)]
#[cfg(stage0)]
#[cfg(windows)]
pub fn init_tls_key() {
- static mut lock: Mutex = MUTEX_INIT;
- static mut initialized: bool = false;
-
unsafe {
- lock.lock();
- if !initialized {
+ tls_lock.lock();
+ if !tls_initialized {
tls::create(&mut RT_TLS_KEY);
- initialized = true;
+ tls_initialized = true;
}
- lock.unlock();
+ tls_lock.unlock();
}
}
#[cfg(not(stage0), not(windows))]
pub fn init_tls_key() {}
+#[cfg(windows)]
+pub unsafe fn cleanup() {
+ // No real use to acquiring a lock around these operations. All we're
+ // going to do is destroy the lock anyway which races locking itself. This
+ // is why the whole function is labeled as 'unsafe'
+ assert!(tls_initialized);
+ tls::destroy(RT_TLS_KEY);
+ tls_lock.destroy();
+ tls_initialized = false;
+}
+
+#[cfg(not(windows))]
+pub unsafe fn cleanup() {
+ assert!(tls_initialized);
+ tls_lock.destroy();
+ tls_initialized = false;
+}
+
/// Give a pointer to thread-local storage.
///
/// # Safety note
init(argc, argv);
let exit_code = run(main);
- cleanup();
+ // unsafe is ok b/c we're sure that the runtime is gone
+ unsafe { cleanup(); }
return exit_code;
}
pub fn start_on_main_thread(argc: int, argv: **u8, main: proc()) -> int {
init(argc, argv);
let exit_code = run_on_main_thread(main);
- cleanup();
+ // unsafe is ok b/c we're sure that the runtime is gone
+ unsafe { cleanup(); }
return exit_code;
}
}
/// One-time runtime cleanup.
-pub fn cleanup() {
+///
+/// This function is unsafe because it performs no checks to ensure that the
+/// runtime has completely ceased running. It is the responsibility of the
+/// caller to ensure that the runtime is entirely shut down and nothing will be
+/// poking around at the internal components.
+///
+/// Invoking cleanup while portions of the runtime are still in use may cause
+/// undefined behavior.
+pub unsafe fn cleanup() {
args::cleanup();
+ local_ptr::cleanup();
}
/// Execute the main function in a scheduler.
pthread_getspecific(key)
}
+#[cfg(unix)]
+pub unsafe fn destroy(key: Key) {
+ assert_eq!(0, pthread_key_delete(key));
+}
+
#[cfg(target_os="macos")]
#[allow(non_camel_case_types)] // foreign type
type pthread_key_t = ::libc::c_ulong;
#[cfg(unix)]
extern {
fn pthread_key_create(key: *mut pthread_key_t, dtor: *u8) -> c_int;
+ fn pthread_key_delete(key: pthread_key_t) -> c_int;
fn pthread_getspecific(key: pthread_key_t) -> *mut c_void;
fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int;
}
TlsGetValue(key)
}
+#[cfg(windows)]
+pub unsafe fn destroy(key: Key) {
+ assert!(TlsFree(key) != 0);
+}
+
#[cfg(windows)]
extern "system" {
fn TlsAlloc() -> DWORD;
+ fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
}