Skip to content

Revert "Revert "std: Re-enable at_exit()"" #23267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions src/liblog/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,9 @@ use std::io::{self, Stderr};
use std::io::prelude::*;
use std::mem;
use std::env;
use std::ptr;
use std::rt;
use std::slice;
use std::sync::{Once, ONCE_INIT};
use std::sync::{Once, ONCE_INIT, StaticMutex, MUTEX_INIT};

use directive::LOG_LEVEL_NAMES;

Expand All @@ -202,6 +201,8 @@ pub const MAX_LOG_LEVEL: u32 = 255;
/// The default logging level of a crate if no other is specified.
const DEFAULT_LOG_LEVEL: u32 = 1;

static LOCK: StaticMutex = MUTEX_INIT;

/// An unsafe constant that is the maximum logging level of any module
/// specified. This is the first line of defense to determining whether a
/// logging statement should be run.
Expand Down Expand Up @@ -286,9 +287,18 @@ impl Drop for DefaultLogger {
pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
// Test the literal string from args against the current filter, if there
// is one.
match unsafe { FILTER.as_ref() } {
Some(filter) if !args.to_string().contains(&filter[..]) => return,
_ => {}
unsafe {
let _g = LOCK.lock();
match FILTER as uint {
0 => {}
1 => panic!("cannot log after main thread has exited"),
n => {
let filter = mem::transmute::<_, &String>(n);
if !args.to_string().contains(&filter[..]) {
return
}
}
}
}

// Completely remove the local logger from TLS in case anyone attempts to
Expand Down Expand Up @@ -370,9 +380,15 @@ pub fn mod_enabled(level: u32, module: &str) -> bool {

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

enabled(level, module, unsafe { (*DIRECTIVES).iter() })
let _g = LOCK.lock();
unsafe {
assert!(DIRECTIVES as uint != 0);
assert!(DIRECTIVES as uint != 1,
"cannot log after the main thread has exited");

enabled(level, module, (*DIRECTIVES).iter())
}
}

fn enabled(level: u32,
Expand Down Expand Up @@ -428,14 +444,14 @@ fn init() {

// Schedule the cleanup for the globals for when the runtime exits.
rt::at_exit(move || {
let _g = LOCK.lock();
assert!(!DIRECTIVES.is_null());
let _directives: Box<Vec<directive::LogDirective>> =
Box::from_raw(DIRECTIVES);
DIRECTIVES = ptr::null_mut();
let _directives = Box::from_raw(DIRECTIVES);
DIRECTIVES = 1 as *mut _;

if !FILTER.is_null() {
let _filter: Box<String> = Box::from_raw(FILTER);
FILTER = 0 as *mut _;
let _filter = Box::from_raw(FILTER);
FILTER = 1 as *mut _;
}
});
}
Expand Down
21 changes: 6 additions & 15 deletions src/libstd/rt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,14 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
}
}

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

Expand All @@ -176,8 +170,5 @@ pub fn at_exit<F:FnOnce()+Send+'static>(f: F) {
pub unsafe fn cleanup() {
args::cleanup();
sys::stack_overflow::cleanup();
// FIXME: (#20012): the resources being cleaned up by at_exit
// currently are not prepared for cleanup to happen asynchronously
// with detached threads using the resources; for now, we leak.
// at_exit_imp::cleanup();
at_exit_imp::cleanup();
}
23 changes: 17 additions & 6 deletions src/libstd/rt/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use intrinsics;
use libc::c_void;
use mem;
use sync::atomic::{self, Ordering};
use sync::{Once, ONCE_INIT};
use sys_common::mutex::{Mutex, MUTEX_INIT};

use rt::libunwind as uw;

Expand Down Expand Up @@ -534,11 +534,22 @@ pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) ->
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
/// }` from ~1900/3700 (-O/no opts) to 180/590.
#[inline(never)] #[cold] // this is the slow path, please never inline this
fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) -> ! {
// Make sure the default panic handler is registered before we look at the
// callbacks.
static INIT: Once = ONCE_INIT;
INIT.call_once(|| unsafe { register(panicking::on_panic); });
fn begin_unwind_inner(msg: Box<Any + Send>,
file_line: &(&'static str, uint)) -> ! {
// Make sure the default failure handler is registered before we look at the
// callbacks. We also use a raw sys-based mutex here instead of a
// `std::sync` one as accessing TLS can cause weird recursive problems (and
// we don't need poison checking).
unsafe {
static LOCK: Mutex = MUTEX_INIT;
static mut INIT: bool = false;
LOCK.lock();
if !INIT {
register(panicking::on_panic);
INIT = true;
}
LOCK.unlock();
}

// First, invoke call the user-defined callbacks triggered on thread panic.
//
Expand Down
19 changes: 13 additions & 6 deletions src/libstd/sys/common/helper_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use prelude::v1::*;

use boxed;
use cell::UnsafeCell;
use ptr;
use rt;
use sync::{StaticMutex, StaticCondvar};
use sync::mpsc::{channel, Sender, Receiver};
Expand Down Expand Up @@ -97,7 +96,7 @@ impl<M: Send> Helper<M> {
{
unsafe {
let _guard = self.lock.lock().unwrap();
if !*self.initialized.get() {
if *self.chan.get() as uint == 0 {
let (tx, rx) = channel();
*self.chan.get() = boxed::into_raw(box tx);
let (receive, send) = helper_signal::new();
Expand All @@ -113,8 +112,10 @@ impl<M: Send> Helper<M> {
self.cond.notify_one()
});

rt::at_exit(move|| { self.shutdown() });
rt::at_exit(move || { self.shutdown() });
*self.initialized.get() = true;
} else if *self.chan.get() as uint == 1 {
panic!("cannot continue usage after shutdown");
}
}
}
Expand All @@ -129,7 +130,9 @@ impl<M: Send> Helper<M> {
// Must send and *then* signal to ensure that the child receives the
// message. Otherwise it could wake up and go to sleep before we
// send the message.
assert!(!self.chan.get().is_null());
assert!(*self.chan.get() as uint != 0);
assert!(*self.chan.get() as uint != 1,
"cannot continue usage after shutdown");
(**self.chan.get()).send(msg).unwrap();
helper_signal::signal(*self.signal.get() as helper_signal::signal);
}
Expand All @@ -142,9 +145,13 @@ impl<M: Send> Helper<M> {
// returns.
let mut guard = self.lock.lock().unwrap();

let ptr = *self.chan.get();
if ptr as uint == 1 {
panic!("cannot continue usage after shutdown");
}
// Close the channel by destroying it
let chan: Box<Sender<M>> = Box::from_raw(*self.chan.get());
*self.chan.get() = ptr::null_mut();
let chan = Box::from_raw(*self.chan.get());
*self.chan.get() = 1 as *mut Sender<M>;
drop(chan);
helper_signal::signal(*self.signal.get() as helper_signal::signal);

Expand Down
4 changes: 0 additions & 4 deletions src/libstd/sys/common/thread_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
use prelude::v1::*;

use sync::atomic::{self, AtomicUsize, Ordering};
use sync::{Mutex, Once, ONCE_INIT};

use sys::thread_local as imp;

Expand Down Expand Up @@ -142,9 +141,6 @@ pub const INIT_INNER: StaticKeyInner = StaticKeyInner {
key: atomic::ATOMIC_USIZE_INIT,
};

static INIT_KEYS: Once = ONCE_INIT;
static mut KEYS: *mut Mutex<Vec<imp::Key>> = 0 as *mut _;

impl StaticKey {
/// Gets the value associated with this TLS key
///
Expand Down
9 changes: 8 additions & 1 deletion src/libstd/sys/unix/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,15 @@ fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
1 => {
loop {
match messages.try_recv() {
// Once we've been disconnected it means the main thread
// is exiting (at_exit has run). We could still have
// active timers for other threads, so we're just going
// to drop them all on the floor. This is all we can
// really do, however, to prevent resource leakage. The
// remaining timers will likely start panicking quickly
// as they attempt to re-use this thread but are
// disallowed to do so.
Err(TryRecvError::Disconnected) => {
assert!(active.len() == 0);
break 'outer;
}

Expand Down
12 changes: 9 additions & 3 deletions src/libstd/sys/windows/thread_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,29 @@ unsafe fn init_dtors() {
rt::at_exit(move|| {
DTOR_LOCK.lock();
let dtors = DTORS;
DTORS = ptr::null_mut();
DTORS = 1 as *mut _;
Box::from_raw(dtors);
assert!(DTORS.is_null()); // can't re-init after destructing
assert!(DTORS as uint == 1); // can't re-init after destructing
DTOR_LOCK.unlock();
});
}

unsafe fn register_dtor(key: Key, dtor: Dtor) {
DTOR_LOCK.lock();
init_dtors();
assert!(DTORS as uint != 0);
assert!(DTORS as uint != 1,
"cannot create new TLS keys after the main thread has exited");
(*DTORS).push((key, dtor));
DTOR_LOCK.unlock();
}

unsafe fn unregister_dtor(key: Key) -> bool {
DTOR_LOCK.lock();
init_dtors();
assert!(DTORS as uint != 0);
assert!(DTORS as uint != 1,
"cannot unregister destructors after the main thread has exited");
let ret = {
let dtors = &mut *DTORS;
let before = dtors.len();
Expand Down Expand Up @@ -241,7 +247,7 @@ unsafe fn run_dtors() {
any_run = false;
let dtors = {
DTOR_LOCK.lock();
let ret = if DTORS.is_null() {
let ret = if DTORS as usize <= 1 {
Vec::new()
} else {
(*DTORS).iter().map(|s| *s).collect()
Expand Down
5 changes: 3 additions & 2 deletions src/libstd/sys/windows/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
None => {}
}
}
// See the comment in unix::timer for why we don't have any
// asserts here and why we're likely just leaving timers on
// the floor as we exit.
Err(TryRecvError::Disconnected) => {
assert_eq!(objs.len(), 1);
assert_eq!(chans.len(), 0);
break 'outer;
}
Err(..) => break
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-fail/rt-set-exit-status-panic2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn r(x:int) -> r {

fn main() {
error!("whatever");
let _t = thread::spawn(move|| {
let _t = thread::scoped(move|| {
let _i = r(5);
});
panic!();
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/unique-send-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn main() {
let _t = (0..n).map(|i| {
expected += i;
let tx = tx.clone();
thread::spawn(move|| {
thread::scoped(move|| {
child(&tx, i)
})
}).collect::<Vec<_>>();
Expand Down