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:
parent
d2368c3c11
commit
9e224c2bf1
39 changed files with 193 additions and 248 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<()> {
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue