Skip to content

Commit ed86b48

Browse files
committed
Clean up statically initialized data on shutdown
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.
1 parent 82d9033 commit ed86b48

File tree

4 files changed

+55
-14
lines changed

4 files changed

+55
-14
lines changed

src/libstd/rt/args.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ pub unsafe fn init(argc: int, argv: **u8) { imp::init(argc, argv) }
3232
pub unsafe fn init(argc: int, argv: **u8) { realargs::init(argc, argv) }
3333

3434
/// One-time global cleanup.
35-
#[cfg(not(test))] pub fn cleanup() { imp::cleanup() }
36-
#[cfg(test)] pub fn cleanup() { realargs::cleanup() }
35+
#[cfg(not(test))] pub unsafe fn cleanup() { imp::cleanup() }
36+
#[cfg(test)] pub unsafe fn cleanup() { realargs::cleanup() }
3737

3838
/// Take the global arguments from global storage.
3939
#[cfg(not(test))] pub fn take() -> Option<~[~str]> { imp::take() }
@@ -74,14 +74,16 @@ mod imp {
7474
use vec;
7575

7676
static mut global_args_ptr: uint = 0;
77+
static mut lock: Mutex = MUTEX_INIT;
7778

7879
pub unsafe fn init(argc: int, argv: **u8) {
7980
let args = load_argc_and_argv(argc, argv);
8081
put(args);
8182
}
8283

83-
pub fn cleanup() {
84+
pub unsafe fn cleanup() {
8485
rtassert!(take().is_some());
86+
lock.destroy();
8587
}
8688

8789
pub fn take() -> Option<~[~str]> {
@@ -108,7 +110,6 @@ mod imp {
108110
}
109111

110112
fn with_lock<T>(f: || -> T) -> T {
111-
static mut lock: Mutex = MUTEX_INIT;
112113
(|| {
113114
unsafe {
114115
lock.lock();

src/libstd/rt/local_ptr.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,45 @@ pub static mut RT_TLS_PTR: *mut c_void = 0 as *mut c_void;
4141
#[cfg(stage0)]
4242
#[cfg(windows)]
4343
static mut RT_TLS_KEY: tls::Key = -1;
44+
static mut tls_lock: Mutex = MUTEX_INIT;
45+
static mut tls_initialized: bool = false;
4446

4547
/// Initialize the TLS key. Other ops will fail if this isn't executed first.
4648
#[inline(never)]
4749
#[cfg(stage0)]
4850
#[cfg(windows)]
4951
pub fn init_tls_key() {
50-
static mut lock: Mutex = MUTEX_INIT;
51-
static mut initialized: bool = false;
52-
5352
unsafe {
54-
lock.lock();
55-
if !initialized {
53+
tls_lock.lock();
54+
if !tls_initialized {
5655
tls::create(&mut RT_TLS_KEY);
57-
initialized = true;
56+
tls_initialized = true;
5857
}
59-
lock.unlock();
58+
tls_lock.unlock();
6059
}
6160
}
6261

6362
#[cfg(not(stage0), not(windows))]
6463
pub fn init_tls_key() {}
6564

65+
#[cfg(windows)]
66+
pub unsafe fn cleanup() {
67+
// No real use to acquiring a lock around these operations. All we're
68+
// going to do is destroy the lock anyway which races locking itself. This
69+
// is why the whole function is labeled as 'unsafe'
70+
assert!(tls_initialized);
71+
tls::destroy(RT_TLS_KEY);
72+
tls_lock.destroy();
73+
tls_initialized = false;
74+
}
75+
76+
#[cfg(not(windows))]
77+
pub unsafe fn cleanup() {
78+
assert!(tls_initialized);
79+
tls_lock.destroy();
80+
tls_initialized = false;
81+
}
82+
6683
/// Give a pointer to thread-local storage.
6784
///
6885
/// # Safety note

src/libstd/rt/mod.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int {
215215

216216
init(argc, argv);
217217
let exit_code = run(main);
218-
cleanup();
218+
// unsafe is ok b/c we're sure that the runtime is gone
219+
unsafe { cleanup(); }
219220

220221
return exit_code;
221222
}
@@ -228,7 +229,8 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int {
228229
pub fn start_on_main_thread(argc: int, argv: **u8, main: proc()) -> int {
229230
init(argc, argv);
230231
let exit_code = run_on_main_thread(main);
231-
cleanup();
232+
// unsafe is ok b/c we're sure that the runtime is gone
233+
unsafe { cleanup(); }
232234

233235
return exit_code;
234236
}
@@ -249,8 +251,17 @@ pub fn init(argc: int, argv: **u8) {
249251
}
250252

251253
/// One-time runtime cleanup.
252-
pub fn cleanup() {
254+
///
255+
/// This function is unsafe because it performs no checks to ensure that the
256+
/// runtime has completely ceased running. It is the responsibility of the
257+
/// caller to ensure that the runtime is entirely shut down and nothing will be
258+
/// poking around at the internal components.
259+
///
260+
/// Invoking cleanup while portions of the runtime are still in use may cause
261+
/// undefined behavior.
262+
pub unsafe fn cleanup() {
253263
args::cleanup();
264+
local_ptr::cleanup();
254265
}
255266

256267
/// Execute the main function in a scheduler.

src/libstd/rt/thread_local_storage.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ pub unsafe fn get(key: Key) -> *mut c_void {
3434
pthread_getspecific(key)
3535
}
3636

37+
#[cfg(unix)]
38+
pub unsafe fn destroy(key: Key) {
39+
assert_eq!(0, pthread_key_delete(key));
40+
}
41+
3742
#[cfg(target_os="macos")]
3843
#[allow(non_camel_case_types)] // foreign type
3944
type pthread_key_t = ::libc::c_ulong;
@@ -47,6 +52,7 @@ type pthread_key_t = ::libc::c_uint;
4752
#[cfg(unix)]
4853
extern {
4954
fn pthread_key_create(key: *mut pthread_key_t, dtor: *u8) -> c_int;
55+
fn pthread_key_delete(key: pthread_key_t) -> c_int;
5056
fn pthread_getspecific(key: pthread_key_t) -> *mut c_void;
5157
fn pthread_setspecific(key: pthread_key_t, value: *mut c_void) -> c_int;
5258
}
@@ -71,9 +77,15 @@ pub unsafe fn get(key: Key) -> *mut c_void {
7177
TlsGetValue(key)
7278
}
7379

80+
#[cfg(windows)]
81+
pub unsafe fn destroy(key: Key) {
82+
assert!(TlsFree(key) != 0);
83+
}
84+
7485
#[cfg(windows)]
7586
extern "system" {
7687
fn TlsAlloc() -> DWORD;
88+
fn TlsFree(dwTlsIndex: DWORD) -> BOOL;
7789
fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID;
7890
fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
7991
}

0 commit comments

Comments
 (0)