Skip to content

Commit 9e224c2

Browse files
committed
std: Re-enable at_exit()
The new semantics of this function are that the callbacks are run when the *main thread* exits, not when all threads have exited. This implies that other threads may still be running when the `at_exit` callbacks are invoked and users need to be prepared for this situation. Users in the standard library have been audited in accordance to these new rules as well. Closes rust-lang#20012
1 parent d2368c3 commit 9e224c2

39 files changed

+192
-247
lines changed

src/liblog/lib.rs

+26-8
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ use std::mem;
177177
use std::os;
178178
use std::rt;
179179
use std::slice;
180-
use std::sync::{Once, ONCE_INIT};
180+
use std::sync::{Once, ONCE_INIT, StaticMutex, MUTEX_INIT};
181181

182182
use regex::Regex;
183183

@@ -193,6 +193,8 @@ pub const MAX_LOG_LEVEL: u32 = 255;
193193
/// The default logging level of a crate if no other is specified.
194194
const DEFAULT_LOG_LEVEL: u32 = 1;
195195

196+
static LOCK: StaticMutex = MUTEX_INIT;
197+
196198
/// An unsafe constant that is the maximum logging level of any module
197199
/// specified. This is the first line of defense to determining whether a
198200
/// logging statement should be run.
@@ -281,9 +283,18 @@ impl Drop for DefaultLogger {
281283
pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
282284
// Test the literal string from args against the current filter, if there
283285
// is one.
284-
match unsafe { FILTER.as_ref() } {
285-
Some(filter) if !filter.is_match(args.to_string()[]) => return,
286-
_ => {}
286+
unsafe {
287+
let _g = LOCK.lock();
288+
match FILTER as uint {
289+
0 => {}
290+
1 => panic!("cannot log after main thread has exited"),
291+
n => {
292+
let filter = mem::transmute::<_, &Regex>(n);
293+
if !filter.is_match(args.to_string().as_slice()) {
294+
return
295+
}
296+
}
297+
}
287298
}
288299

289300
// Completely remove the local logger from TLS in case anyone attempts to
@@ -401,9 +412,15 @@ pub fn mod_enabled(level: u32, module: &str) -> bool {
401412

402413
// This assertion should never get tripped unless we're in an at_exit
403414
// handler after logging has been torn down and a logging attempt was made.
404-
assert!(unsafe { !DIRECTIVES.is_null() });
405415

406-
enabled(level, module, unsafe { (*DIRECTIVES).iter() })
416+
let _g = LOCK.lock();
417+
unsafe {
418+
assert!(DIRECTIVES as uint != 0);
419+
assert!(DIRECTIVES as uint != 1,
420+
"cannot log after the main thread has exited");
421+
422+
enabled(level, module, (*DIRECTIVES).iter())
423+
}
407424
}
408425

409426
fn enabled(level: u32,
@@ -459,14 +476,15 @@ fn init() {
459476

460477
// Schedule the cleanup for the globals for when the runtime exits.
461478
rt::at_exit(move |:| {
479+
let _g = LOCK.lock();
462480
assert!(!DIRECTIVES.is_null());
463481
let _directives: Box<Vec<directive::LogDirective>> =
464482
mem::transmute(DIRECTIVES);
465-
DIRECTIVES = 0 as *const Vec<directive::LogDirective>;
483+
DIRECTIVES = 1 as *const Vec<directive::LogDirective>;
466484

467485
if !FILTER.is_null() {
468486
let _filter: Box<Regex> = mem::transmute(FILTER);
469-
FILTER = 0 as *const _;
487+
FILTER = 1 as *const _;
470488
}
471489
});
472490
}

src/libstd/io/stdio.rs

+19-24
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,19 @@
2626
//! ```
2727
2828
use self::StdSource::*;
29+
use prelude::*;
2930

30-
use boxed::Box;
3131
use cell::RefCell;
32-
use clone::Clone;
3332
use failure::LOCAL_STDERR;
3433
use fmt;
35-
use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
36-
standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
37-
use kinds::{Sync, Send};
34+
use io::{IoResult, IoError, OtherIoError};
35+
use io::{standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
3836
use libc;
3937
use mem;
40-
use option::Option;
41-
use option::Option::{Some, None};
42-
use ops::{Deref, DerefMut, FnOnce};
43-
use result::Result::{Ok, Err};
4438
use rt;
45-
use slice::SliceExt;
46-
use str::StrExt;
47-
use string::String;
4839
use sys::{fs, tty};
49-
use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
40+
use sync::{Arc, Mutex, MutexGuard, StaticMutex, MUTEX_INIT};
5041
use uint;
51-
use vec::Vec;
5242

5343
// And so begins the tale of acquiring a uv handle to a stdio stream on all
5444
// platforms in all situations. Our story begins by splitting the world into two
@@ -215,14 +205,15 @@ impl Reader for StdinReader {
215205
pub fn stdin() -> StdinReader {
216206
// We're following the same strategy as kimundi's lazy_static library
217207
static mut STDIN: *const StdinReader = 0 as *const StdinReader;
218-
static ONCE: Once = ONCE_INIT;
208+
static LOCK: StaticMutex = MUTEX_INIT;
219209

220210
unsafe {
221-
ONCE.doit(|| {
222-
// The default buffer capacity is 64k, but apparently windows doesn't like
223-
// 64k reads on stdin. See #13304 for details, but the idea is that on
224-
// windows we use a slightly smaller buffer that's been seen to be
225-
// acceptable.
211+
let _g = LOCK.lock();
212+
if STDIN as uint == 0 {
213+
// The default buffer capacity is 64k, but apparently windows
214+
// doesn't like 64k reads on stdin. See #13304 for details, but the
215+
// idea is that on windows we use a slightly smaller buffer that's
216+
// been seen to be acceptable.
226217
let stdin = if cfg!(windows) {
227218
BufferedReader::with_capacity(8 * 1024, stdin_raw())
228219
} else {
@@ -235,11 +226,15 @@ pub fn stdin() -> StdinReader {
235226

236227
// Make sure to free it at exit
237228
rt::at_exit(|| {
238-
mem::transmute::<_, Box<StdinReader>>(STDIN);
239-
STDIN = 0 as *const _;
229+
let g = LOCK.lock();
230+
let stdin = STDIN;
231+
STDIN = 1 as *const _;
232+
drop(g);
233+
mem::transmute::<_, Box<StdinReader>>(stdin);
240234
});
241-
});
242-
235+
} else if STDIN as uint == 1 {
236+
panic!("accessing stdin after the main thread has exited")
237+
}
243238
(*STDIN).clone()
244239
}
245240
}

src/libstd/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,13 @@ pub mod thread;
228228
pub mod sync;
229229
pub mod comm;
230230

231+
#[path = "sys/common/mod.rs"] mod sys_common;
232+
231233
#[cfg(unix)]
232234
#[path = "sys/unix/mod.rs"] mod sys;
233235
#[cfg(windows)]
234236
#[path = "sys/windows/mod.rs"] mod sys;
235237

236-
#[path = "sys/common/mod.rs"] mod sys_common;
237-
238238
pub mod rt;
239239
mod failure;
240240

src/libstd/rt/at_exit_imp.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type Queue = Vec<Thunk>;
2929
static LOCK: Mutex = MUTEX_INIT;
3030
static mut QUEUE: *mut Queue = 0 as *mut Queue;
3131

32+
const DTOR_RUN_ITERS: uint = 10;
33+
3234
unsafe fn init() {
3335
if QUEUE.is_null() {
3436
let state: Box<Queue> = box Vec::new();
@@ -49,7 +51,7 @@ pub fn cleanup() {
4951
unsafe {
5052
LOCK.lock();
5153
let queue = QUEUE;
52-
QUEUE = 1 as *mut _;
54+
QUEUE = 1u as *mut _;
5355
LOCK.unlock();
5456

5557
// make sure we're not recursively cleaning up

src/libstd/rt/mod.rs

+7-18
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
9292
// but we just do this to name the main thread and to give it correct
9393
// info about the stack bounds.
9494
let thread: Thread = NewThread::new(Some("<main>".to_string()));
95-
thread_info::set((my_stack_bottom, my_stack_top),
96-
sys::thread::guard::main(),
97-
thread);
95+
thread_info::set(sys::thread::guard::main(), thread);
9896

9997
// By default, some platforms will send a *signal* when a EPIPE error
10098
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
@@ -133,20 +131,14 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
133131
}
134132
}
135133

136-
/// Enqueues a procedure to run when the runtime is cleaned up
137-
///
138-
/// The procedure passed to this function will be executed as part of the
139-
/// runtime cleanup phase. For normal rust programs, this means that it will run
140-
/// after all other threads have exited.
141-
///
142-
/// The procedure is *not* executed with a local `Thread` available to it, so
143-
/// primitives like logging, I/O, channels, spawning, etc, are *not* available.
144-
/// This is meant for "bare bones" usage to clean up runtime details, this is
145-
/// not meant as a general-purpose "let's clean everything up" function.
134+
/// Enqueues a procedure to run when the main thread exits.
146135
///
147136
/// It is forbidden for procedures to register more `at_exit` handlers when they
148137
/// are running, and doing so will lead to a process abort.
149-
pub fn at_exit<F:FnOnce()+Send>(f: F) {
138+
///
139+
/// Note that other threads may still be running when `at_exit` routines start
140+
/// running.
141+
pub fn at_exit<F: FnOnce() + Send>(f: F) {
150142
at_exit_imp::push(Thunk::new(f));
151143
}
152144

@@ -162,8 +154,5 @@ pub fn at_exit<F:FnOnce()+Send>(f: F) {
162154
pub unsafe fn cleanup() {
163155
args::cleanup();
164156
sys::stack_overflow::cleanup();
165-
// FIXME: (#20012): the resources being cleaned up by at_exit
166-
// currently are not prepared for cleanup to happen asynchronously
167-
// with detached threads using the resources; for now, we leak.
168-
// at_exit_imp::cleanup();
157+
at_exit_imp::cleanup();
169158
}

src/libstd/rt/unwind.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ use intrinsics;
6868
use libc::c_void;
6969
use mem;
7070
use sync::atomic;
71-
use sync::{Once, ONCE_INIT};
71+
use sys_common::mutex::{Mutex, MUTEX_INIT};
7272

7373
use rt::libunwind as uw;
7474

@@ -587,11 +587,20 @@ pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) ->
587587
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
588588
/// }` from ~1900/3700 (-O/no opts) to 180/590.
589589
#[inline(never)] #[cold] // this is the slow path, please never inline this
590-
fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) -> ! {
590+
fn begin_unwind_inner(msg: Box<Any + Send>,
591+
file_line: &(&'static str, uint)) -> ! {
591592
// Make sure the default failure handler is registered before we look at the
592593
// callbacks.
593-
static INIT: Once = ONCE_INIT;
594-
INIT.doit(|| unsafe { register(failure::on_fail); });
594+
unsafe {
595+
static LOCK: Mutex = MUTEX_INIT;
596+
static mut INIT: bool = false;
597+
LOCK.lock();
598+
if !INIT {
599+
register(failure::on_fail);
600+
INIT = true;
601+
}
602+
LOCK.unlock();
603+
}
595604

596605
// First, invoke call the user-defined callbacks triggered on thread panic.
597606
//

src/libstd/sys/common/helper_thread.rs

+26-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
//! can be created in the future and there must be no active timers at that
2121
//! time.
2222
23+
#![macro_escape]
24+
2325
use prelude::*;
2426

2527
use cell::UnsafeCell;
@@ -68,6 +70,17 @@ struct RaceBox(helper_signal::signal);
6870
unsafe impl Send for RaceBox {}
6971
unsafe impl Sync for RaceBox {}
7072

73+
macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
74+
static $name: Helper<$m> = Helper {
75+
lock: ::sync::MUTEX_INIT,
76+
cond: ::sync::CONDVAR_INIT,
77+
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
78+
signal: ::cell::UnsafeCell { value: 0 },
79+
initialized: ::cell::UnsafeCell { value: false },
80+
shutdown: ::cell::UnsafeCell { value: false },
81+
};
82+
) }
83+
7184
impl<M: Send> Helper<M> {
7285
/// Lazily boots a helper thread, becoming a no-op if the helper has already
7386
/// been spawned.
@@ -84,7 +97,7 @@ impl<M: Send> Helper<M> {
8497
{
8598
unsafe {
8699
let _guard = self.lock.lock().unwrap();
87-
if !*self.initialized.get() {
100+
if *self.chan.get() as uint == 0 {
88101
let (tx, rx) = channel();
89102
*self.chan.get() = mem::transmute(box tx);
90103
let (receive, send) = helper_signal::new();
@@ -93,15 +106,17 @@ impl<M: Send> Helper<M> {
93106
let receive = RaceBox(receive);
94107

95108
let t = f();
96-
Thread::spawn(move |:| {
109+
Thread::spawn(move || {
97110
helper(receive.0, rx, t);
98111
let _g = self.lock.lock().unwrap();
99112
*self.shutdown.get() = true;
100113
self.cond.notify_one()
101114
}).detach();
102115

103-
rt::at_exit(move|:| { self.shutdown() });
116+
rt::at_exit(move || { self.shutdown() });
104117
*self.initialized.get() = true;
118+
} else if *self.chan.get() as uint == 1 {
119+
panic!("cannot continue usage after shutdown");
105120
}
106121
}
107122
}
@@ -116,7 +131,9 @@ impl<M: Send> Helper<M> {
116131
// Must send and *then* signal to ensure that the child receives the
117132
// message. Otherwise it could wake up and go to sleep before we
118133
// send the message.
119-
assert!(!self.chan.get().is_null());
134+
assert!(*self.chan.get() as uint != 0);
135+
assert!(*self.chan.get() as uint != 1,
136+
"cannot continue usage after shutdown");
120137
(**self.chan.get()).send(msg);
121138
helper_signal::signal(*self.signal.get() as helper_signal::signal);
122139
}
@@ -129,9 +146,13 @@ impl<M: Send> Helper<M> {
129146
// returns.
130147
let mut guard = self.lock.lock().unwrap();
131148

149+
let ptr = *self.chan.get();
150+
if ptr as uint == 1 {
151+
panic!("cannot continue usage after shutdown");
152+
}
132153
// Close the channel by destroying it
133154
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
134-
*self.chan.get() = 0 as *mut Sender<M>;
155+
*self.chan.get() = 1 as *mut Sender<M>;
135156
drop(chan);
136157
helper_signal::signal(*self.signal.get() as helper_signal::signal);
137158

src/libstd/sys/common/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
#![allow(missing_docs)]
12-
#![allow(dead_code)]
12+
#![macro_escape]
1313

1414
use io::{mod, IoError, IoResult};
1515
use prelude::*;

src/libstd/sys/common/mutex.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl Mutex {
2929
/// Behavior is undefined if the mutex is moved after the first method is
3030
/// called on the mutex.
3131
#[inline]
32+
#[allow(dead_code)] // sys is not exported yet
3233
pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) }
3334

3435
/// Lock the mutex blocking the current thread until it is available.

src/libstd/sys/common/net.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ use io::{IoResult, IoError};
2222
use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
2323
wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
2424
decode_error_detailed};
25-
use sync::{Mutex, MutexGuard};
25+
use sync::Mutex;
26+
#[cfg(not(target_os = "linux"))]
27+
use sync::MutexGuard;
2628
use sys_common::{mod, keep_going, short_write, timeout};
2729
use prelude::*;
2830
use cmp;
@@ -573,11 +575,13 @@ impl Drop for Inner {
573575
fn drop(&mut self) { unsafe { close_sock(self.fd); } }
574576
}
575577

578+
#[cfg(not(target_os = "linux"))]
576579
pub struct Guard<'a> {
577580
pub fd: sock_t,
578581
pub guard: MutexGuard<'a, ()>,
579582
}
580583

584+
#[cfg(not(target_os = "linux"))]
581585
#[unsafe_destructor]
582586
impl<'a> Drop for Guard<'a> {
583587
fn drop(&mut self) {

src/libstd/sys/common/rwlock.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl RWLock {
2626
/// Usage of an RWLock is undefined if it is moved after its first use (any
2727
/// function calls below).
2828
#[inline]
29+
#[allow(dead_code)] // sys is not exported yet
2930
pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) }
3031

3132
/// Acquire shared access to the underlying lock, blocking the current

0 commit comments

Comments
 (0)