]> git.lizzy.rs Git - rust.git/commitdiff
Clean up statically initialized data on shutdown
authorAlex Crichton <alex@alexcrichton.com>
Tue, 26 Nov 2013 02:27:27 +0000 (18:27 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 27 Nov 2013 05:11:17 +0000 (21:11 -0800)
Whenever the runtime is shut down, add a few hooks to clean up some of the
statically initialized data of the runtime. Note that this is an unsafe
operation because there's no guarantee on behalf of the runtime that there's no
other code running which is using the runtime.

This helps turn down the noise a bit in the valgrind output related to
statically initialized mutexes. It doesn't turn the noise down to 0 because
there are still statically initialized mutexes in dynamic_lib and
os::with_env_lock, but I believe that it would be easy enough to add exceptions
for those cases and I don't think that it's the runtime's job to go and clean up
that data.

src/libstd/rt/args.rs
src/libstd/rt/local_ptr.rs
src/libstd/rt/mod.rs
src/libstd/rt/thread_local_storage.rs

index 43e8096a8b113a6aa34640e3591865178fc39f9f..7b27161ab5d7c9fd7954ee3d2d604fd13ef05f05 100644 (file)
@@ -32,8 +32,8 @@ pub unsafe fn init(argc: int, argv: **u8) { imp::init(argc, argv) }
 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() }
@@ -74,14 +74,16 @@ mod imp {
     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]> {
@@ -108,7 +110,6 @@ pub fn clone() -> Option<~[~str]> {
     }
 
     fn with_lock<T>(f: || -> T) -> T {
-        static mut lock: Mutex = MUTEX_INIT;
         (|| {
             unsafe {
                 lock.lock();
index e0e8750e146fc308c47eb690418b2d13742fd969..86f0f643c996251cc814be283b13fc9d6b5825bc 100644 (file)
 #[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
index 860b65b20c6654100fb707fecf20ef91afba8b56..79b7dbf2aabf4f42fe47b51f4c9bc15672b4d553 100644 (file)
@@ -215,7 +215,8 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int {
 
     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;
 }
@@ -228,7 +229,8 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int {
 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;
 }
@@ -249,8 +251,17 @@ pub fn init(argc: int, argv: **u8) {
 }
 
 /// 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.
index 8fa64852846a836bee4ec75e9354c2dbedb2980c..62e1b6c50d65f82ec73004513396e3f2520dbc62 100644 (file)
@@ -34,6 +34,11 @@ pub unsafe fn get(key: Key) -> *mut c_void {
     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;
@@ -47,6 +52,7 @@ pub unsafe fn get(key: Key) -> *mut c_void {
 #[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;
 }
@@ -71,9 +77,15 @@ pub unsafe fn get(key: Key) -> *mut c_void {
     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;
 }