diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 89c7b294512a..600d0bb133ef 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -40,3 +40,39 @@ macro_rules! rtabort( } ) ) +macro_rules! assert_once_ever( + ($( $msg:expr),+) => ( { + // FIXME(#8472) extra function should not be needed to hide unsafe + fn assert_once_ever() { + unsafe { + static mut already_happened: int = 0; + // Double-check lock to avoid a swap in the common case. + if already_happened != 0 || + ::unstable::intrinsics::atomic_xchg_relaxed(&mut already_happened, 1) != 0 { + fail!(fmt!("assert_once_ever happened twice: %s", fmt!($($msg),+))); + } + } + } + assert_once_ever(); + } ) +) + +#[cfg(test)] +mod tests { + #[test] + fn test_assert_once_ever_ok() { + assert_once_ever!("help i'm stuck in an"); + assert_once_ever!("assertion error message"); + } + + #[test] #[ignore(cfg(windows))] #[should_fail] + fn test_assert_once_ever_fail() { + use task; + + fn f() { assert_once_ever!("if you're seeing this... good!") } + + // linked & watched, naturally + task::spawn(f); + task::spawn(f); + } +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index db1bfdf1bf56..8b3e65b57ab7 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -323,6 +323,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int { // task tree, shut down the schedulers and set the exit code. let handles = Cell::new(handles); let on_exit: ~fn(bool) = |exit_success| { + assert_once_ever!("last task exiting"); let mut handles = handles.take(); for handle in handles.mut_iter() {