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 #20012
This commit is contained in:
Alex Crichton 2014-12-19 11:29:39 -08:00
parent d2368c3c11
commit 9e224c2bf1
39 changed files with 193 additions and 248 deletions

View file

@ -7,19 +7,22 @@
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// As always, windows has something very different than unix, we mainly want
/// to avoid having to depend too much on libunwind for windows.
///
/// If you google around, you'll find a fair bit of references to built-in
/// functions to get backtraces on windows. It turns out that most of these are
/// in an external library called dbghelp. I was unable to find this library
/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
/// of it.
///
/// You'll also find that there's a function called CaptureStackBackTrace
/// mentioned frequently (which is also easy to use), but sadly I didn't have a
/// copy of that function in my mingw install (maybe it was broken?). Instead,
/// this takes the route of using StackWalk64 in order to walk the stack.
//! As always, windows has something very different than unix, we mainly want
//! to avoid having to depend too much on libunwind for windows.
//!
//! If you google around, you'll find a fair bit of references to built-in
//! functions to get backtraces on windows. It turns out that most of these are
//! in an external library called dbghelp. I was unable to find this library
//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
//! of it.
//!
//! You'll also find that there's a function called CaptureStackBackTrace
//! mentioned frequently (which is also easy to use), but sadly I didn't have a
//! copy of that function in my mingw install (maybe it was broken?). Instead,
//! this takes the route of using StackWalk64 in order to walk the stack.
#![allow(dead_code)] // constants/fields aren't always used on all platforms
use c_str::CString;
use intrinsics;
@ -294,7 +297,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
// According to windows documentation, all dbghelp functions are
// single-threaded.
static LOCK: StaticMutex = MUTEX_INIT;
let _g = unsafe { LOCK.lock() };
let _g = LOCK.lock();
// Open up dbghelp.dll, we don't link to it explicitly because it can't
// always be found. Additionally, it's nice having fewer dependencies.

View file

@ -15,7 +15,6 @@
#![allow(non_camel_case_types)]
use libc;
use prelude::*;
pub const WSADESCRIPTION_LEN: uint = 256;
pub const WSASYS_STATUS_LEN: uint = 128;

View file

@ -10,21 +10,17 @@
//! Blocking Windows-based file I/O
use alloc::arc::Arc;
use libc::{mod, c_int};
use c_str::CString;
use mem;
use sys::os::fill_utf16_buf_and_decode;
use path;
use ptr;
use str;
use io;
use mem;
use ptr;
use sys::os::fill_utf16_buf_and_decode;
use prelude::*;
use sys;
use sys::os;
use sys_common::{keep_going, eof, mkerr_libc};
use sys_common::{unimpl, mkerr_libc};
use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
use io::{IoResult, IoError, FileStat, SeekStyle};
@ -445,7 +441,7 @@ pub fn stat(p: &Path) -> IoResult<FileStat> {
// FIXME: move this to platform-specific modules (for now)?
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
// FIXME: implementation is missing
Err(super::unimpl())
Err(unimpl())
}
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {

View file

@ -11,30 +11,14 @@
#![allow(missing_docs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unused_unsafe)]
#![allow(unused_mut)]
extern crate libc;
use num;
use mem;
use prelude::*;
use io::{mod, IoResult, IoError};
use sync::{Once, ONCE_INIT};
macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => (
static $name: Helper<$m> = Helper {
lock: ::sync::MUTEX_INIT,
cond: ::sync::CONDVAR_INIT,
chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
signal: ::cell::UnsafeCell { value: 0 },
initialized: ::cell::UnsafeCell { value: false },
shutdown: ::cell::UnsafeCell { value: false },
};
) }
pub mod backtrace;
pub mod c;
pub mod ext;
@ -179,14 +163,6 @@ pub fn init_net() {
}
}
pub fn unimpl() -> IoError {
IoError {
kind: io::IoUnavailable,
desc: "operation is not implemented",
detail: None,
}
}
pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
match s {
Some(s) => Ok({

View file

@ -15,14 +15,12 @@
use prelude::*;
use fmt;
use io::{IoResult, IoError};
use libc::{c_int, c_char, c_void};
use libc::{c_int, c_void};
use libc;
use os;
use path::BytesContainer;
use ptr;
use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
use sys::fs::FileDesc;
use slice;

View file

@ -365,7 +365,7 @@ impl UnixStream {
// acquire the lock.
//
// See comments in close_read() about why this lock is necessary.
let guard = unsafe { self.inner.lock.lock() };
let guard = self.inner.lock.lock();
if self.read_closed() {
return Err(eof())
}
@ -441,7 +441,7 @@ impl UnixStream {
// going after we woke up.
//
// See comments in close_read() about why this lock is necessary.
let guard = unsafe { self.inner.lock.lock() };
let guard = self.inner.lock.lock();
if self.write_closed() {
return Err(epipe())
}
@ -516,14 +516,14 @@ impl UnixStream {
// close_read() between steps 1 and 2. By atomically executing steps 1
// and 2 with a lock with respect to close_read(), we're guaranteed that
// no thread will erroneously sit in a read forever.
let _guard = unsafe { self.inner.lock.lock() };
let _guard = self.inner.lock.lock();
self.inner.read_closed.store(true, atomic::SeqCst);
self.cancel_io()
}
pub fn close_write(&mut self) -> IoResult<()> {
// see comments in close_read() for why this lock is necessary
let _guard = unsafe { self.inner.lock.lock() };
let _guard = self.inner.lock.lock();
self.inner.write_closed.store(true, atomic::SeqCst);
self.cancel_io()
}

View file

@ -8,25 +8,24 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use libc::{pid_t, c_void, c_int};
use prelude::*;
use libc::{pid_t, c_void};
use libc;
use c_str::CString;
use io;
use mem;
use os;
use ptr;
use prelude::*;
use io::process::{ProcessExit, ExitStatus, ExitSignal};
use io::process::{ProcessExit, ExitStatus};
use collections;
use path::BytesContainer;
use hash::Hash;
use io::{IoResult, IoError};
use sys::fs;
use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
use sys::timer;
use sys::fs::FileDesc;
use sys_common::helper_thread::Helper;
use sys_common::{AsInner, mkerr_libc, timeout};
use sys_common::{AsInner, timeout};
use io::fs::PathExtensions;
@ -121,8 +120,6 @@ impl Process {
use libc::funcs::extra::msvcrt::get_osfhandle;
use mem;
use iter::{Iterator, IteratorExt};
use str::StrExt;
if cfg.gid().is_some() || cfg.uid().is_some() {
return Err(IoError {

View file

@ -14,7 +14,7 @@ use ptr;
use mem;
use libc;
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
use sys_common::{stack, thread_info};
use sys_common::stack;
pub struct Handler {
_data: *mut libc::c_void
@ -30,14 +30,6 @@ impl Drop for Handler {
fn drop(&mut self) {}
}
// get_task_info is called from an exception / signal handler.
// It returns the guard page of the current task or 0 if that
// guard page doesn't exist. None is returned if there's currently
// no local task.
unsafe fn get_task_guard_page() -> uint {
thread_info::stack_guard()
}
// This is initialized in init() and only read from after
static mut PAGE_SIZE: uint = 0;

View file

@ -14,11 +14,10 @@ use libc;
use mem;
use ptr;
use prelude::*;
use super::{last_error, last_net_error, retry, sock_t};
use super::{last_error, last_net_error, sock_t};
use sync::{Arc, atomic};
use sys::fs::FileDesc;
use sys::{mod, c, set_nonblocking, wouldblock, timer};
use sys_common::{mod, timeout, eof, net};
use sys_common::{timeout, eof, net};
pub use sys_common::net::TcpStream;
@ -205,10 +204,6 @@ impl TcpAcceptor {
Err(eof())
}
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
net::sockname(self.socket(), libc::getsockname)
}
pub fn set_timeout(&mut self, timeout: Option<u64>) {
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
}

View file

@ -8,8 +8,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::prelude::*;
use boxed::Box;
use cmp;
use mem;

View file

@ -137,9 +137,9 @@ unsafe fn init_dtors() {
rt::at_exit(move|| {
DTOR_LOCK.lock();
let dtors = DTORS;
DTORS = 0 as *mut _;
DTORS = 1 as *mut _;
mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(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();
});
}
@ -147,6 +147,9 @@ unsafe fn init_dtors() {
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();
}
@ -154,6 +157,9 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) {
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();
@ -232,6 +238,7 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID,
}
}
#[allow(dead_code)] // not actually dead
unsafe fn run_dtors() {
let mut any_run = true;
for _ in range(0, 5i) {

View file

@ -26,8 +26,6 @@ use libc;
use ptr;
use comm;
use sys::c;
use sys::fs::FileDesc;
use sys_common::helper_thread::Helper;
use prelude::*;
use io::IoResult;
@ -80,9 +78,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(comm::Disconnected) => {
assert_eq!(objs.len(), 1);
assert_eq!(chans.len(), 0);
break 'outer;
}
Err(..) => break

View file

@ -26,7 +26,6 @@
//! to working in raw UTF-16, with such a wrapper around it.
use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode};
use super::c::{ERROR_ILLEGAL_CHARACTER};
use super::c::{ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS};
use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
@ -38,6 +37,8 @@ use prelude::*;
use ptr;
use str::from_utf8;
use sys_common::unimpl;
fn invalid_encoding() -> IoError {
IoError {
kind: io::InvalidInput,
@ -149,11 +150,8 @@ impl TTY {
// Make a CONSOLE_SCREEN_BUFFER_INFO
// Call GetConsoleScreenBufferInfo
// Maybe call GetLargestConsoleWindowSize instead?
Err(super::unimpl())
Err(unimpl())
}
// Let us magically declare this as a TTY
pub fn isatty(&self) -> bool { true }
}
impl Drop for TTY {