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
|
@ -177,7 +177,7 @@ use std::mem;
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::rt;
|
use std::rt;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::{Once, ONCE_INIT, StaticMutex, MUTEX_INIT};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
|
@ -193,6 +193,8 @@ pub const MAX_LOG_LEVEL: u32 = 255;
|
||||||
/// The default logging level of a crate if no other is specified.
|
/// The default logging level of a crate if no other is specified.
|
||||||
const DEFAULT_LOG_LEVEL: u32 = 1;
|
const DEFAULT_LOG_LEVEL: u32 = 1;
|
||||||
|
|
||||||
|
static LOCK: StaticMutex = MUTEX_INIT;
|
||||||
|
|
||||||
/// An unsafe constant that is the maximum logging level of any module
|
/// An unsafe constant that is the maximum logging level of any module
|
||||||
/// specified. This is the first line of defense to determining whether a
|
/// specified. This is the first line of defense to determining whether a
|
||||||
/// logging statement should be run.
|
/// logging statement should be run.
|
||||||
|
@ -281,9 +283,18 @@ impl Drop for DefaultLogger {
|
||||||
pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
|
pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
|
||||||
// Test the literal string from args against the current filter, if there
|
// Test the literal string from args against the current filter, if there
|
||||||
// is one.
|
// is one.
|
||||||
match unsafe { FILTER.as_ref() } {
|
unsafe {
|
||||||
Some(filter) if !filter.is_match(args.to_string()[]) => return,
|
let _g = LOCK.lock();
|
||||||
_ => {}
|
match FILTER as uint {
|
||||||
|
0 => {}
|
||||||
|
1 => panic!("cannot log after main thread has exited"),
|
||||||
|
n => {
|
||||||
|
let filter = mem::transmute::<_, &Regex>(n);
|
||||||
|
if !filter.is_match(args.to_string().as_slice()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Completely remove the local logger from TLS in case anyone attempts to
|
// 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 {
|
||||||
|
|
||||||
// This assertion should never get tripped unless we're in an at_exit
|
// 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.
|
// 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,
|
fn enabled(level: u32,
|
||||||
|
@ -459,14 +476,15 @@ fn init() {
|
||||||
|
|
||||||
// Schedule the cleanup for the globals for when the runtime exits.
|
// Schedule the cleanup for the globals for when the runtime exits.
|
||||||
rt::at_exit(move |:| {
|
rt::at_exit(move |:| {
|
||||||
|
let _g = LOCK.lock();
|
||||||
assert!(!DIRECTIVES.is_null());
|
assert!(!DIRECTIVES.is_null());
|
||||||
let _directives: Box<Vec<directive::LogDirective>> =
|
let _directives: Box<Vec<directive::LogDirective>> =
|
||||||
mem::transmute(DIRECTIVES);
|
mem::transmute(DIRECTIVES);
|
||||||
DIRECTIVES = 0 as *const Vec<directive::LogDirective>;
|
DIRECTIVES = 1 as *const Vec<directive::LogDirective>;
|
||||||
|
|
||||||
if !FILTER.is_null() {
|
if !FILTER.is_null() {
|
||||||
let _filter: Box<Regex> = mem::transmute(FILTER);
|
let _filter: Box<Regex> = mem::transmute(FILTER);
|
||||||
FILTER = 0 as *const _;
|
FILTER = 1 as *const _;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,29 +26,19 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use self::StdSource::*;
|
use self::StdSource::*;
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
use boxed::Box;
|
|
||||||
use cell::RefCell;
|
use cell::RefCell;
|
||||||
use clone::Clone;
|
|
||||||
use failure::LOCAL_STDERR;
|
use failure::LOCAL_STDERR;
|
||||||
use fmt;
|
use fmt;
|
||||||
use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
|
use io::{IoResult, IoError, OtherIoError};
|
||||||
standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
|
use io::{standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
|
||||||
use kinds::{Sync, Send};
|
|
||||||
use libc;
|
use libc;
|
||||||
use mem;
|
use mem;
|
||||||
use option::Option;
|
|
||||||
use option::Option::{Some, None};
|
|
||||||
use ops::{Deref, DerefMut, FnOnce};
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use rt;
|
use rt;
|
||||||
use slice::SliceExt;
|
|
||||||
use str::StrExt;
|
|
||||||
use string::String;
|
|
||||||
use sys::{fs, tty};
|
use sys::{fs, tty};
|
||||||
use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
|
use sync::{Arc, Mutex, MutexGuard, StaticMutex, MUTEX_INIT};
|
||||||
use uint;
|
use uint;
|
||||||
use vec::Vec;
|
|
||||||
|
|
||||||
// And so begins the tale of acquiring a uv handle to a stdio stream on all
|
// And so begins the tale of acquiring a uv handle to a stdio stream on all
|
||||||
// platforms in all situations. Our story begins by splitting the world into two
|
// platforms in all situations. Our story begins by splitting the world into two
|
||||||
|
@ -215,14 +205,15 @@ impl Reader for StdinReader {
|
||||||
pub fn stdin() -> StdinReader {
|
pub fn stdin() -> StdinReader {
|
||||||
// We're following the same strategy as kimundi's lazy_static library
|
// We're following the same strategy as kimundi's lazy_static library
|
||||||
static mut STDIN: *const StdinReader = 0 as *const StdinReader;
|
static mut STDIN: *const StdinReader = 0 as *const StdinReader;
|
||||||
static ONCE: Once = ONCE_INIT;
|
static LOCK: StaticMutex = MUTEX_INIT;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
ONCE.doit(|| {
|
let _g = LOCK.lock();
|
||||||
// The default buffer capacity is 64k, but apparently windows doesn't like
|
if STDIN as uint == 0 {
|
||||||
// 64k reads on stdin. See #13304 for details, but the idea is that on
|
// The default buffer capacity is 64k, but apparently windows
|
||||||
// windows we use a slightly smaller buffer that's been seen to be
|
// doesn't like 64k reads on stdin. See #13304 for details, but the
|
||||||
// acceptable.
|
// idea is that on windows we use a slightly smaller buffer that's
|
||||||
|
// been seen to be acceptable.
|
||||||
let stdin = if cfg!(windows) {
|
let stdin = if cfg!(windows) {
|
||||||
BufferedReader::with_capacity(8 * 1024, stdin_raw())
|
BufferedReader::with_capacity(8 * 1024, stdin_raw())
|
||||||
} else {
|
} else {
|
||||||
|
@ -235,11 +226,15 @@ pub fn stdin() -> StdinReader {
|
||||||
|
|
||||||
// Make sure to free it at exit
|
// Make sure to free it at exit
|
||||||
rt::at_exit(|| {
|
rt::at_exit(|| {
|
||||||
mem::transmute::<_, Box<StdinReader>>(STDIN);
|
let g = LOCK.lock();
|
||||||
STDIN = 0 as *const _;
|
let stdin = STDIN;
|
||||||
|
STDIN = 1 as *const _;
|
||||||
|
drop(g);
|
||||||
|
mem::transmute::<_, Box<StdinReader>>(stdin);
|
||||||
});
|
});
|
||||||
});
|
} else if STDIN as uint == 1 {
|
||||||
|
panic!("accessing stdin after the main thread has exited")
|
||||||
|
}
|
||||||
(*STDIN).clone()
|
(*STDIN).clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,13 +228,13 @@ pub mod thread;
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod comm;
|
pub mod comm;
|
||||||
|
|
||||||
|
#[path = "sys/common/mod.rs"] mod sys_common;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[path = "sys/unix/mod.rs"] mod sys;
|
#[path = "sys/unix/mod.rs"] mod sys;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[path = "sys/windows/mod.rs"] mod sys;
|
#[path = "sys/windows/mod.rs"] mod sys;
|
||||||
|
|
||||||
#[path = "sys/common/mod.rs"] mod sys_common;
|
|
||||||
|
|
||||||
pub mod rt;
|
pub mod rt;
|
||||||
mod failure;
|
mod failure;
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,8 @@ type Queue = Vec<Thunk>;
|
||||||
static LOCK: Mutex = MUTEX_INIT;
|
static LOCK: Mutex = MUTEX_INIT;
|
||||||
static mut QUEUE: *mut Queue = 0 as *mut Queue;
|
static mut QUEUE: *mut Queue = 0 as *mut Queue;
|
||||||
|
|
||||||
|
const DTOR_RUN_ITERS: uint = 10;
|
||||||
|
|
||||||
unsafe fn init() {
|
unsafe fn init() {
|
||||||
if QUEUE.is_null() {
|
if QUEUE.is_null() {
|
||||||
let state: Box<Queue> = box Vec::new();
|
let state: Box<Queue> = box Vec::new();
|
||||||
|
@ -49,7 +51,7 @@ pub fn cleanup() {
|
||||||
unsafe {
|
unsafe {
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
let queue = QUEUE;
|
let queue = QUEUE;
|
||||||
QUEUE = 1 as *mut _;
|
QUEUE = 1u as *mut _;
|
||||||
LOCK.unlock();
|
LOCK.unlock();
|
||||||
|
|
||||||
// make sure we're not recursively cleaning up
|
// make sure we're not recursively cleaning up
|
||||||
|
|
|
@ -92,9 +92,7 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
|
||||||
// but we just do this to name the main thread and to give it correct
|
// but we just do this to name the main thread and to give it correct
|
||||||
// info about the stack bounds.
|
// info about the stack bounds.
|
||||||
let thread: Thread = NewThread::new(Some("<main>".to_string()));
|
let thread: Thread = NewThread::new(Some("<main>".to_string()));
|
||||||
thread_info::set((my_stack_bottom, my_stack_top),
|
thread_info::set(sys::thread::guard::main(), thread);
|
||||||
sys::thread::guard::main(),
|
|
||||||
thread);
|
|
||||||
|
|
||||||
// By default, some platforms will send a *signal* when a EPIPE error
|
// By default, some platforms will send a *signal* when a EPIPE error
|
||||||
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
|
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
|
||||||
|
@ -133,19 +131,13 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enqueues a procedure to run when the runtime is cleaned up
|
/// Enqueues a procedure to run when the main thread exits.
|
||||||
///
|
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
/// It is forbidden for procedures to register more `at_exit` handlers when they
|
/// It is forbidden for procedures to register more `at_exit` handlers when they
|
||||||
/// are running, and doing so will lead to a process abort.
|
/// are running, and doing so will lead to a process abort.
|
||||||
|
///
|
||||||
|
/// Note that other threads may still be running when `at_exit` routines start
|
||||||
|
/// running.
|
||||||
pub fn at_exit<F: FnOnce() + Send>(f: F) {
|
pub fn at_exit<F: FnOnce() + Send>(f: F) {
|
||||||
at_exit_imp::push(Thunk::new(f));
|
at_exit_imp::push(Thunk::new(f));
|
||||||
}
|
}
|
||||||
|
@ -162,8 +154,5 @@ pub fn at_exit<F:FnOnce()+Send>(f: F) {
|
||||||
pub unsafe fn cleanup() {
|
pub unsafe fn cleanup() {
|
||||||
args::cleanup();
|
args::cleanup();
|
||||||
sys::stack_overflow::cleanup();
|
sys::stack_overflow::cleanup();
|
||||||
// FIXME: (#20012): the resources being cleaned up by at_exit
|
at_exit_imp::cleanup();
|
||||||
// currently are not prepared for cleanup to happen asynchronously
|
|
||||||
// with detached threads using the resources; for now, we leak.
|
|
||||||
// at_exit_imp::cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ use intrinsics;
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use mem;
|
use mem;
|
||||||
use sync::atomic;
|
use sync::atomic;
|
||||||
use sync::{Once, ONCE_INIT};
|
use sys_common::mutex::{Mutex, MUTEX_INIT};
|
||||||
|
|
||||||
use rt::libunwind as uw;
|
use rt::libunwind as uw;
|
||||||
|
|
||||||
|
@ -587,11 +587,20 @@ 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!()
|
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
|
||||||
/// }` from ~1900/3700 (-O/no opts) to 180/590.
|
/// }` from ~1900/3700 (-O/no opts) to 180/590.
|
||||||
#[inline(never)] #[cold] // this is the slow path, please never inline this
|
#[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)) -> ! {
|
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
|
// Make sure the default failure handler is registered before we look at the
|
||||||
// callbacks.
|
// callbacks.
|
||||||
static INIT: Once = ONCE_INIT;
|
unsafe {
|
||||||
INIT.doit(|| unsafe { register(failure::on_fail); });
|
static LOCK: Mutex = MUTEX_INIT;
|
||||||
|
static mut INIT: bool = false;
|
||||||
|
LOCK.lock();
|
||||||
|
if !INIT {
|
||||||
|
register(failure::on_fail);
|
||||||
|
INIT = true;
|
||||||
|
}
|
||||||
|
LOCK.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
// First, invoke call the user-defined callbacks triggered on thread panic.
|
// First, invoke call the user-defined callbacks triggered on thread panic.
|
||||||
//
|
//
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
//! can be created in the future and there must be no active timers at that
|
//! can be created in the future and there must be no active timers at that
|
||||||
//! time.
|
//! time.
|
||||||
|
|
||||||
|
#![macro_escape]
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
|
@ -68,6 +70,17 @@ struct RaceBox(helper_signal::signal);
|
||||||
unsafe impl Send for RaceBox {}
|
unsafe impl Send for RaceBox {}
|
||||||
unsafe impl Sync for RaceBox {}
|
unsafe impl Sync for RaceBox {}
|
||||||
|
|
||||||
|
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 },
|
||||||
|
};
|
||||||
|
) }
|
||||||
|
|
||||||
impl<M: Send> Helper<M> {
|
impl<M: Send> Helper<M> {
|
||||||
/// Lazily boots a helper thread, becoming a no-op if the helper has already
|
/// Lazily boots a helper thread, becoming a no-op if the helper has already
|
||||||
/// been spawned.
|
/// been spawned.
|
||||||
|
@ -84,7 +97,7 @@ impl<M: Send> Helper<M> {
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let _guard = self.lock.lock().unwrap();
|
let _guard = self.lock.lock().unwrap();
|
||||||
if !*self.initialized.get() {
|
if *self.chan.get() as uint == 0 {
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
*self.chan.get() = mem::transmute(box tx);
|
*self.chan.get() = mem::transmute(box tx);
|
||||||
let (receive, send) = helper_signal::new();
|
let (receive, send) = helper_signal::new();
|
||||||
|
@ -93,15 +106,17 @@ impl<M: Send> Helper<M> {
|
||||||
let receive = RaceBox(receive);
|
let receive = RaceBox(receive);
|
||||||
|
|
||||||
let t = f();
|
let t = f();
|
||||||
Thread::spawn(move |:| {
|
Thread::spawn(move || {
|
||||||
helper(receive.0, rx, t);
|
helper(receive.0, rx, t);
|
||||||
let _g = self.lock.lock().unwrap();
|
let _g = self.lock.lock().unwrap();
|
||||||
*self.shutdown.get() = true;
|
*self.shutdown.get() = true;
|
||||||
self.cond.notify_one()
|
self.cond.notify_one()
|
||||||
}).detach();
|
}).detach();
|
||||||
|
|
||||||
rt::at_exit(move|:| { self.shutdown() });
|
rt::at_exit(move || { self.shutdown() });
|
||||||
*self.initialized.get() = true;
|
*self.initialized.get() = true;
|
||||||
|
} else if *self.chan.get() as uint == 1 {
|
||||||
|
panic!("cannot continue usage after shutdown");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +131,9 @@ impl<M: Send> Helper<M> {
|
||||||
// Must send and *then* signal to ensure that the child receives the
|
// Must send and *then* signal to ensure that the child receives the
|
||||||
// message. Otherwise it could wake up and go to sleep before we
|
// message. Otherwise it could wake up and go to sleep before we
|
||||||
// send the message.
|
// 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);
|
(**self.chan.get()).send(msg);
|
||||||
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
||||||
}
|
}
|
||||||
|
@ -129,9 +146,13 @@ impl<M: Send> Helper<M> {
|
||||||
// returns.
|
// returns.
|
||||||
let mut guard = self.lock.lock().unwrap();
|
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
|
// Close the channel by destroying it
|
||||||
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
|
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
|
||||||
*self.chan.get() = 0 as *mut Sender<M>;
|
*self.chan.get() = 1 as *mut Sender<M>;
|
||||||
drop(chan);
|
drop(chan);
|
||||||
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
helper_signal::signal(*self.signal.get() as helper_signal::signal);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
#![allow(dead_code)]
|
#![macro_escape]
|
||||||
|
|
||||||
use io::{mod, IoError, IoResult};
|
use io::{mod, IoError, IoResult};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
|
@ -29,6 +29,7 @@ impl Mutex {
|
||||||
/// Behavior is undefined if the mutex is moved after the first method is
|
/// Behavior is undefined if the mutex is moved after the first method is
|
||||||
/// called on the mutex.
|
/// called on the mutex.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(dead_code)] // sys is not exported yet
|
||||||
pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) }
|
pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) }
|
||||||
|
|
||||||
/// Lock the mutex blocking the current thread until it is available.
|
/// Lock the mutex blocking the current thread until it is available.
|
||||||
|
|
|
@ -22,7 +22,9 @@ use io::{IoResult, IoError};
|
||||||
use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
|
use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
|
||||||
wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
|
wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
|
||||||
decode_error_detailed};
|
decode_error_detailed};
|
||||||
use sync::{Mutex, MutexGuard};
|
use sync::Mutex;
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
use sync::MutexGuard;
|
||||||
use sys_common::{mod, keep_going, short_write, timeout};
|
use sys_common::{mod, keep_going, short_write, timeout};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use cmp;
|
use cmp;
|
||||||
|
@ -573,11 +575,13 @@ impl Drop for Inner {
|
||||||
fn drop(&mut self) { unsafe { close_sock(self.fd); } }
|
fn drop(&mut self) { unsafe { close_sock(self.fd); } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
pub struct Guard<'a> {
|
pub struct Guard<'a> {
|
||||||
pub fd: sock_t,
|
pub fd: sock_t,
|
||||||
pub guard: MutexGuard<'a, ()>,
|
pub guard: MutexGuard<'a, ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
#[unsafe_destructor]
|
#[unsafe_destructor]
|
||||||
impl<'a> Drop for Guard<'a> {
|
impl<'a> Drop for Guard<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ impl RWLock {
|
||||||
/// Usage of an RWLock is undefined if it is moved after its first use (any
|
/// Usage of an RWLock is undefined if it is moved after its first use (any
|
||||||
/// function calls below).
|
/// function calls below).
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[allow(dead_code)] // sys is not exported yet
|
||||||
pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) }
|
pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) }
|
||||||
|
|
||||||
/// Acquire shared access to the underlying lock, blocking the current
|
/// Acquire shared access to the underlying lock, blocking the current
|
||||||
|
|
|
@ -121,37 +121,6 @@ pub unsafe fn record_os_managed_stack_bounds(stack_lo: uint, _stack_hi: uint) {
|
||||||
record_sp_limit(stack_lo + RED_ZONE);
|
record_sp_limit(stack_lo + RED_ZONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn record_rust_managed_stack_bounds(stack_lo: uint, stack_hi: uint) {
|
|
||||||
// When the old runtime had segmented stacks, it used a calculation that was
|
|
||||||
// "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic
|
|
||||||
// symbol resolution, llvm function calls, etc. In theory this red zone
|
|
||||||
// value is 0, but it matters far less when we have gigantic stacks because
|
|
||||||
// we don't need to be so exact about our stack budget. The "fudge factor"
|
|
||||||
// was because LLVM doesn't emit a stack check for functions < 256 bytes in
|
|
||||||
// size. Again though, we have giant stacks, so we round all these
|
|
||||||
// calculations up to the nice round number of 20k.
|
|
||||||
record_sp_limit(stack_lo + RED_ZONE);
|
|
||||||
|
|
||||||
return target_record_stack_bounds(stack_lo, stack_hi);
|
|
||||||
|
|
||||||
#[cfg(not(windows))] #[inline(always)]
|
|
||||||
unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {}
|
|
||||||
|
|
||||||
#[cfg(all(windows, target_arch = "x86"))] #[inline(always)]
|
|
||||||
unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
|
|
||||||
// stack range is at TIB: %fs:0x04 (top) and %fs:0x08 (bottom)
|
|
||||||
asm!("mov $0, %fs:0x04" :: "r"(stack_hi) :: "volatile");
|
|
||||||
asm!("mov $0, %fs:0x08" :: "r"(stack_lo) :: "volatile");
|
|
||||||
}
|
|
||||||
#[cfg(all(windows, target_arch = "x86_64"))] #[inline(always)]
|
|
||||||
unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) {
|
|
||||||
// stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom)
|
|
||||||
asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile");
|
|
||||||
asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Records the current limit of the stack as specified by `end`.
|
/// Records the current limit of the stack as specified by `end`.
|
||||||
///
|
///
|
||||||
/// This is stored in an OS-dependent location, likely inside of the thread
|
/// This is stored in an OS-dependent location, likely inside of the thread
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(dead_code)] // stack_guard isn't used right now on all platforms
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use thread::Thread;
|
use thread::Thread;
|
||||||
|
@ -15,10 +17,6 @@ use cell::RefCell;
|
||||||
use string::String;
|
use string::String;
|
||||||
|
|
||||||
struct ThreadInfo {
|
struct ThreadInfo {
|
||||||
// This field holds the known bounds of the stack in (lo, hi)
|
|
||||||
// form. Not all threads necessarily know their precise bounds,
|
|
||||||
// hence this is optional.
|
|
||||||
stack_bounds: (uint, uint),
|
|
||||||
stack_guard: uint,
|
stack_guard: uint,
|
||||||
thread: Thread,
|
thread: Thread,
|
||||||
}
|
}
|
||||||
|
@ -35,7 +33,6 @@ impl ThreadInfo {
|
||||||
THREAD_INFO.with(|c| {
|
THREAD_INFO.with(|c| {
|
||||||
if c.borrow().is_none() {
|
if c.borrow().is_none() {
|
||||||
*c.borrow_mut() = Some(ThreadInfo {
|
*c.borrow_mut() = Some(ThreadInfo {
|
||||||
stack_bounds: (0, 0),
|
|
||||||
stack_guard: 0,
|
stack_guard: 0,
|
||||||
thread: NewThread::new(None),
|
thread: NewThread::new(None),
|
||||||
})
|
})
|
||||||
|
@ -53,10 +50,9 @@ pub fn stack_guard() -> uint {
|
||||||
ThreadInfo::with(|info| info.stack_guard)
|
ThreadInfo::with(|info| info.stack_guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) {
|
pub fn set(stack_guard: uint, thread: Thread) {
|
||||||
THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
|
THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
|
||||||
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{
|
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{
|
||||||
stack_bounds: stack_bounds,
|
|
||||||
stack_guard: stack_guard,
|
stack_guard: stack_guard,
|
||||||
thread: thread,
|
thread: thread,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -55,11 +55,11 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(dead_code)] // sys isn't exported yet
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use sync::atomic::{mod, AtomicUint};
|
use sync::atomic::{mod, AtomicUint};
|
||||||
use sync::{Mutex, Once, ONCE_INIT};
|
|
||||||
|
|
||||||
use sys::thread_local as imp;
|
use sys::thread_local as imp;
|
||||||
|
|
||||||
|
@ -140,9 +140,6 @@ pub const INIT_INNER: StaticKeyInner = StaticKeyInner {
|
||||||
key: atomic::INIT_ATOMIC_UINT,
|
key: atomic::INIT_ATOMIC_UINT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static INIT_KEYS: Once = ONCE_INIT;
|
|
||||||
static mut KEYS: *mut Mutex<Vec<imp::Key>> = 0 as *mut _;
|
|
||||||
|
|
||||||
impl StaticKey {
|
impl StaticKey {
|
||||||
/// Gets the value associated with this TLS key
|
/// Gets the value associated with this TLS key
|
||||||
///
|
///
|
||||||
|
|
|
@ -83,12 +83,12 @@
|
||||||
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
|
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
|
||||||
/// all unix platforms we support right now, so it at least gets the job done.
|
/// all unix platforms we support right now, so it at least gets the job done.
|
||||||
|
|
||||||
|
use prelude::*;
|
||||||
|
|
||||||
use c_str::CString;
|
use c_str::CString;
|
||||||
use io::{IoResult, Writer};
|
use io::IoResult;
|
||||||
use libc;
|
use libc;
|
||||||
use mem;
|
use mem;
|
||||||
use option::Option::{mod, Some, None};
|
|
||||||
use result::Result::{Ok, Err};
|
|
||||||
use sync::{StaticMutex, MUTEX_INIT};
|
use sync::{StaticMutex, MUTEX_INIT};
|
||||||
|
|
||||||
use sys_common::backtrace::*;
|
use sys_common::backtrace::*;
|
||||||
|
@ -151,7 +151,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
|
||||||
// I/O done here is blocking I/O, not green I/O, so we don't have to
|
// I/O done here is blocking I/O, not green I/O, so we don't have to
|
||||||
// worry about this being a native vs green mutex.
|
// worry about this being a native vs green mutex.
|
||||||
static LOCK: StaticMutex = MUTEX_INIT;
|
static LOCK: StaticMutex = MUTEX_INIT;
|
||||||
let _g = unsafe { LOCK.lock() };
|
let _g = LOCK.lock();
|
||||||
|
|
||||||
try!(writeln!(w, "stack backtrace:"));
|
try!(writeln!(w, "stack backtrace:"));
|
||||||
|
|
||||||
|
@ -241,12 +241,8 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||||
fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
|
fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> {
|
||||||
use iter::{Iterator, IteratorExt};
|
|
||||||
use os;
|
use os;
|
||||||
use path::GenericPath;
|
|
||||||
use ptr::PtrExt;
|
|
||||||
use ptr;
|
use ptr;
|
||||||
use slice::SliceExt;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
// libbacktrace.h API
|
// libbacktrace.h API
|
||||||
|
|
|
@ -10,30 +10,14 @@
|
||||||
|
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_unsafe)]
|
|
||||||
#![allow(unused_mut)]
|
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use num;
|
|
||||||
use num::{Int, SignedInt};
|
use num::{Int, SignedInt};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use io::{mod, IoResult, IoError};
|
use io::{mod, IoResult, IoError};
|
||||||
use sys_common::mkerr_libc;
|
use sys_common::mkerr_libc;
|
||||||
|
|
||||||
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 backtrace;
|
||||||
pub mod c;
|
pub mod c;
|
||||||
pub mod ext;
|
pub mod ext;
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
use cell::UnsafeCell;
|
use cell::UnsafeCell;
|
||||||
use kinds::Sync;
|
use kinds::Sync;
|
||||||
use sys::sync as ffi;
|
use sys::sync as ffi;
|
||||||
use sys_common::mutex;
|
|
||||||
|
|
||||||
pub struct Mutex { inner: UnsafeCell<ffi::pthread_mutex_t> }
|
pub struct Mutex { inner: UnsafeCell<ffi::pthread_mutex_t> }
|
||||||
|
|
||||||
|
@ -26,6 +25,7 @@ pub const MUTEX_INIT: Mutex = Mutex {
|
||||||
|
|
||||||
unsafe impl Sync for Mutex {}
|
unsafe impl Sync for Mutex {}
|
||||||
|
|
||||||
|
#[allow(dead_code)] // sys isn't exported yet
|
||||||
impl Mutex {
|
impl Mutex {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn new() -> Mutex {
|
pub unsafe fn new() -> Mutex {
|
||||||
|
|
|
@ -10,17 +10,16 @@
|
||||||
|
|
||||||
//! Implementation of `std::os` functionality for unix systems
|
//! Implementation of `std::os` functionality for unix systems
|
||||||
|
|
||||||
|
#![allow(unused_imports)] // lots of cfg code here
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use error::{FromError, Error};
|
|
||||||
use fmt;
|
|
||||||
use io::{IoError, IoResult};
|
use io::{IoError, IoResult};
|
||||||
use libc::{mod, c_int, c_char, c_void};
|
use libc::{mod, c_int, c_char};
|
||||||
|
use os;
|
||||||
use path::BytesContainer;
|
use path::BytesContainer;
|
||||||
use ptr;
|
use ptr;
|
||||||
use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
|
|
||||||
use sys::fs::FileDesc;
|
use sys::fs::FileDesc;
|
||||||
use os;
|
|
||||||
|
|
||||||
use os::TMPBUF_SZ;
|
use os::TMPBUF_SZ;
|
||||||
|
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl UnixStream {
|
||||||
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
|
fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
|
||||||
let ret = Guard {
|
let ret = Guard {
|
||||||
fd: self.fd(),
|
fd: self.fd(),
|
||||||
guard: unsafe { self.inner.lock.lock().unwrap() },
|
guard: self.inner.lock.lock().unwrap(),
|
||||||
};
|
};
|
||||||
assert!(set_nonblocking(self.fd(), true).is_ok());
|
assert!(set_nonblocking(self.fd(), true).is_ok());
|
||||||
ret
|
ret
|
||||||
|
|
|
@ -11,7 +11,7 @@ use self::Req::*;
|
||||||
|
|
||||||
use libc::{mod, pid_t, c_void, c_int};
|
use libc::{mod, pid_t, c_void, c_int};
|
||||||
use c_str::CString;
|
use c_str::CString;
|
||||||
use io::{mod, IoResult, IoError, EndOfFile};
|
use io::{IoResult, EndOfFile};
|
||||||
use mem;
|
use mem;
|
||||||
use os;
|
use os;
|
||||||
use ptr;
|
use ptr;
|
||||||
|
@ -327,7 +327,7 @@ impl Process {
|
||||||
// The actual communication between the helper thread and this thread is
|
// The actual communication between the helper thread and this thread is
|
||||||
// quite simple, just a channel moving data around.
|
// quite simple, just a channel moving data around.
|
||||||
|
|
||||||
unsafe { HELPER.boot(register_sigchld, waitpid_helper) }
|
HELPER.boot(register_sigchld, waitpid_helper);
|
||||||
|
|
||||||
match self.try_wait() {
|
match self.try_wait() {
|
||||||
Some(ret) => return Ok(ret),
|
Some(ret) => return Ok(ret),
|
||||||
|
@ -335,7 +335,7 @@ impl Process {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
unsafe { HELPER.send(NewChild(self.pid, tx, deadline)); }
|
HELPER.send(NewChild(self.pid, tx, deadline));
|
||||||
return match rx.recv_opt() {
|
return match rx.recv_opt() {
|
||||||
Ok(e) => Ok(e),
|
Ok(e) => Ok(e),
|
||||||
Err(()) => Err(timeout("wait timed out")),
|
Err(()) => Err(timeout("wait timed out")),
|
||||||
|
@ -419,8 +419,15 @@ impl Process {
|
||||||
Ok(NewChild(pid, tx, deadline)) => {
|
Ok(NewChild(pid, tx, deadline)) => {
|
||||||
active.push((pid, tx, deadline));
|
active.push((pid, tx, deadline));
|
||||||
}
|
}
|
||||||
|
// Once we've been disconnected it means the main
|
||||||
|
// thread is exiting (at_exit has run). We could
|
||||||
|
// still have active waiter for other threads, so
|
||||||
|
// we're just going to drop them all on the floor.
|
||||||
|
// This means that they won't receive a "you're
|
||||||
|
// done" message in which case they'll be considered
|
||||||
|
// as timed out, but more generally errors will
|
||||||
|
// start propagating.
|
||||||
Err(comm::Disconnected) => {
|
Err(comm::Disconnected) => {
|
||||||
assert!(active.len() == 0);
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
Err(comm::Empty) => break,
|
Err(comm::Empty) => break,
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub const RWLOCK_INIT: RWLock = RWLock {
|
||||||
inner: UnsafeCell { value: ffi::PTHREAD_RWLOCK_INITIALIZER },
|
inner: UnsafeCell { value: ffi::PTHREAD_RWLOCK_INITIALIZER },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)] // sys isn't exported yet
|
||||||
impl RWLock {
|
impl RWLock {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn new() -> RWLock {
|
pub unsafe fn new() -> RWLock {
|
||||||
|
|
|
@ -34,7 +34,6 @@ impl Drop for Handler {
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
mod imp {
|
mod imp {
|
||||||
use core::prelude::*;
|
|
||||||
use sys_common::stack;
|
use sys_common::stack;
|
||||||
|
|
||||||
use super::Handler;
|
use super::Handler;
|
||||||
|
|
|
@ -135,10 +135,6 @@ impl TcpAcceptor {
|
||||||
Err(sys_common::eof())
|
Err(sys_common::eof())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
|
|
||||||
net::sockname(self.fd(), libc::getsockname)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||||
self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
|
self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ pub fn now() -> u64 {
|
||||||
fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
|
fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
|
||||||
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
let mut set: c::fd_set = unsafe { mem::zeroed() };
|
||||||
|
|
||||||
let mut fd = FileDesc::new(input, true);
|
let fd = FileDesc::new(input, true);
|
||||||
let mut timeout: libc::timeval = unsafe { mem::zeroed() };
|
let mut timeout: libc::timeval = unsafe { mem::zeroed() };
|
||||||
|
|
||||||
// active timers are those which are able to be selected upon (and it's a
|
// active timers are those which are able to be selected upon (and it's a
|
||||||
|
@ -168,8 +168,15 @@ fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
|
||||||
1 => {
|
1 => {
|
||||||
loop {
|
loop {
|
||||||
match messages.try_recv() {
|
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(comm::Disconnected) => {
|
Err(comm::Disconnected) => {
|
||||||
assert!(active.len() == 0);
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,5 +43,4 @@ impl TTY {
|
||||||
pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
|
||||||
Err(sys_common::unimpl())
|
Err(sys_common::unimpl())
|
||||||
}
|
}
|
||||||
pub fn isatty(&self) -> bool { false }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,22 @@
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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.
|
//! 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
|
//! If you google around, you'll find a fair bit of references to built-in
|
||||||
/// in an external library called dbghelp. I was unable to find this library
|
//! functions to get backtraces on windows. It turns out that most of these are
|
||||||
/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
|
//! in an external library called dbghelp. I was unable to find this library
|
||||||
/// of it.
|
//! 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
|
//! You'll also find that there's a function called CaptureStackBackTrace
|
||||||
/// copy of that function in my mingw install (maybe it was broken?). Instead,
|
//! mentioned frequently (which is also easy to use), but sadly I didn't have a
|
||||||
/// this takes the route of using StackWalk64 in order to walk the stack.
|
//! 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 c_str::CString;
|
||||||
use intrinsics;
|
use intrinsics;
|
||||||
|
@ -294,7 +297,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> {
|
||||||
// According to windows documentation, all dbghelp functions are
|
// According to windows documentation, all dbghelp functions are
|
||||||
// single-threaded.
|
// single-threaded.
|
||||||
static LOCK: StaticMutex = MUTEX_INIT;
|
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
|
// 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.
|
// always be found. Additionally, it's nice having fewer dependencies.
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use libc;
|
use libc;
|
||||||
use prelude::*;
|
|
||||||
|
|
||||||
pub const WSADESCRIPTION_LEN: uint = 256;
|
pub const WSADESCRIPTION_LEN: uint = 256;
|
||||||
pub const WSASYS_STATUS_LEN: uint = 128;
|
pub const WSASYS_STATUS_LEN: uint = 128;
|
||||||
|
|
|
@ -10,21 +10,17 @@
|
||||||
|
|
||||||
//! Blocking Windows-based file I/O
|
//! Blocking Windows-based file I/O
|
||||||
|
|
||||||
use alloc::arc::Arc;
|
|
||||||
use libc::{mod, c_int};
|
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 io;
|
||||||
|
use mem;
|
||||||
|
use ptr;
|
||||||
|
use sys::os::fill_utf16_buf_and_decode;
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use sys;
|
use sys;
|
||||||
use sys::os;
|
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::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
|
||||||
use io::{IoResult, IoError, FileStat, SeekStyle};
|
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)?
|
// FIXME: move this to platform-specific modules (for now)?
|
||||||
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
|
pub fn lstat(_p: &Path) -> IoResult<FileStat> {
|
||||||
// FIXME: implementation is missing
|
// FIXME: implementation is missing
|
||||||
Err(super::unimpl())
|
Err(unimpl())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||||
|
|
|
@ -11,30 +11,14 @@
|
||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(unused_imports)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_unsafe)]
|
|
||||||
#![allow(unused_mut)]
|
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
use num;
|
|
||||||
use mem;
|
use mem;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use io::{mod, IoResult, IoError};
|
use io::{mod, IoResult, IoError};
|
||||||
use sync::{Once, ONCE_INIT};
|
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 backtrace;
|
||||||
pub mod c;
|
pub mod c;
|
||||||
pub mod ext;
|
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>> {
|
pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
|
||||||
match s {
|
match s {
|
||||||
Some(s) => Ok({
|
Some(s) => Ok({
|
||||||
|
|
|
@ -15,14 +15,12 @@
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
use fmt;
|
|
||||||
use io::{IoResult, IoError};
|
use io::{IoResult, IoError};
|
||||||
use libc::{c_int, c_char, c_void};
|
use libc::{c_int, c_void};
|
||||||
use libc;
|
use libc;
|
||||||
use os;
|
use os;
|
||||||
use path::BytesContainer;
|
use path::BytesContainer;
|
||||||
use ptr;
|
use ptr;
|
||||||
use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst};
|
|
||||||
use sys::fs::FileDesc;
|
use sys::fs::FileDesc;
|
||||||
use slice;
|
use slice;
|
||||||
|
|
||||||
|
|
|
@ -365,7 +365,7 @@ impl UnixStream {
|
||||||
// acquire the lock.
|
// acquire the lock.
|
||||||
//
|
//
|
||||||
// See comments in close_read() about why this lock is necessary.
|
// 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() {
|
if self.read_closed() {
|
||||||
return Err(eof())
|
return Err(eof())
|
||||||
}
|
}
|
||||||
|
@ -441,7 +441,7 @@ impl UnixStream {
|
||||||
// going after we woke up.
|
// going after we woke up.
|
||||||
//
|
//
|
||||||
// See comments in close_read() about why this lock is necessary.
|
// 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() {
|
if self.write_closed() {
|
||||||
return Err(epipe())
|
return Err(epipe())
|
||||||
}
|
}
|
||||||
|
@ -516,14 +516,14 @@ impl UnixStream {
|
||||||
// close_read() between steps 1 and 2. By atomically executing steps 1
|
// 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
|
// and 2 with a lock with respect to close_read(), we're guaranteed that
|
||||||
// no thread will erroneously sit in a read forever.
|
// 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.inner.read_closed.store(true, atomic::SeqCst);
|
||||||
self.cancel_io()
|
self.cancel_io()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_write(&mut self) -> IoResult<()> {
|
pub fn close_write(&mut self) -> IoResult<()> {
|
||||||
// see comments in close_read() for why this lock is necessary
|
// 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.inner.write_closed.store(true, atomic::SeqCst);
|
||||||
self.cancel_io()
|
self.cancel_io()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,25 +8,24 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use libc::{pid_t, c_void, c_int};
|
use prelude::*;
|
||||||
|
|
||||||
|
use libc::{pid_t, c_void};
|
||||||
use libc;
|
use libc;
|
||||||
use c_str::CString;
|
use c_str::CString;
|
||||||
use io;
|
use io;
|
||||||
use mem;
|
use mem;
|
||||||
use os;
|
use os;
|
||||||
use ptr;
|
use ptr;
|
||||||
use prelude::*;
|
use io::process::{ProcessExit, ExitStatus};
|
||||||
use io::process::{ProcessExit, ExitStatus, ExitSignal};
|
|
||||||
use collections;
|
use collections;
|
||||||
use path::BytesContainer;
|
use path::BytesContainer;
|
||||||
use hash::Hash;
|
use hash::Hash;
|
||||||
use io::{IoResult, IoError};
|
use io::{IoResult, IoError};
|
||||||
|
|
||||||
use sys::fs;
|
use sys::timer;
|
||||||
use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
|
|
||||||
use sys::fs::FileDesc;
|
use sys::fs::FileDesc;
|
||||||
use sys_common::helper_thread::Helper;
|
use sys_common::{AsInner, timeout};
|
||||||
use sys_common::{AsInner, mkerr_libc, timeout};
|
|
||||||
|
|
||||||
use io::fs::PathExtensions;
|
use io::fs::PathExtensions;
|
||||||
|
|
||||||
|
@ -121,8 +120,6 @@ impl Process {
|
||||||
use libc::funcs::extra::msvcrt::get_osfhandle;
|
use libc::funcs::extra::msvcrt::get_osfhandle;
|
||||||
|
|
||||||
use mem;
|
use mem;
|
||||||
use iter::{Iterator, IteratorExt};
|
|
||||||
use str::StrExt;
|
|
||||||
|
|
||||||
if cfg.gid().is_some() || cfg.uid().is_some() {
|
if cfg.gid().is_some() || cfg.uid().is_some() {
|
||||||
return Err(IoError {
|
return Err(IoError {
|
||||||
|
|
|
@ -14,7 +14,7 @@ use ptr;
|
||||||
use mem;
|
use mem;
|
||||||
use libc;
|
use libc;
|
||||||
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
|
use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL};
|
||||||
use sys_common::{stack, thread_info};
|
use sys_common::stack;
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
_data: *mut libc::c_void
|
_data: *mut libc::c_void
|
||||||
|
@ -30,14 +30,6 @@ impl Drop for Handler {
|
||||||
fn drop(&mut self) {}
|
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
|
// This is initialized in init() and only read from after
|
||||||
static mut PAGE_SIZE: uint = 0;
|
static mut PAGE_SIZE: uint = 0;
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,10 @@ use libc;
|
||||||
use mem;
|
use mem;
|
||||||
use ptr;
|
use ptr;
|
||||||
use prelude::*;
|
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 sync::{Arc, atomic};
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys::{mod, c, set_nonblocking, wouldblock, timer};
|
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;
|
pub use sys_common::net::TcpStream;
|
||||||
|
|
||||||
|
@ -205,10 +204,6 @@ impl TcpAcceptor {
|
||||||
Err(eof())
|
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>) {
|
pub fn set_timeout(&mut self, timeout: Option<u64>) {
|
||||||
self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
|
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
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use core::prelude::*;
|
|
||||||
|
|
||||||
use boxed::Box;
|
use boxed::Box;
|
||||||
use cmp;
|
use cmp;
|
||||||
use mem;
|
use mem;
|
||||||
|
|
|
@ -137,9 +137,9 @@ unsafe fn init_dtors() {
|
||||||
rt::at_exit(move|| {
|
rt::at_exit(move|| {
|
||||||
DTOR_LOCK.lock();
|
DTOR_LOCK.lock();
|
||||||
let dtors = DTORS;
|
let dtors = DTORS;
|
||||||
DTORS = 0 as *mut _;
|
DTORS = 1 as *mut _;
|
||||||
mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
|
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();
|
DTOR_LOCK.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,9 @@ unsafe fn init_dtors() {
|
||||||
unsafe fn register_dtor(key: Key, dtor: Dtor) {
|
unsafe fn register_dtor(key: Key, dtor: Dtor) {
|
||||||
DTOR_LOCK.lock();
|
DTOR_LOCK.lock();
|
||||||
init_dtors();
|
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));
|
(*DTORS).push((key, dtor));
|
||||||
DTOR_LOCK.unlock();
|
DTOR_LOCK.unlock();
|
||||||
}
|
}
|
||||||
|
@ -154,6 +157,9 @@ unsafe fn register_dtor(key: Key, dtor: Dtor) {
|
||||||
unsafe fn unregister_dtor(key: Key) -> bool {
|
unsafe fn unregister_dtor(key: Key) -> bool {
|
||||||
DTOR_LOCK.lock();
|
DTOR_LOCK.lock();
|
||||||
init_dtors();
|
init_dtors();
|
||||||
|
assert!(DTORS as uint != 0);
|
||||||
|
assert!(DTORS as uint != 1,
|
||||||
|
"cannot unregister destructors after the main thread has exited");
|
||||||
let ret = {
|
let ret = {
|
||||||
let dtors = &mut *DTORS;
|
let dtors = &mut *DTORS;
|
||||||
let before = dtors.len();
|
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() {
|
unsafe fn run_dtors() {
|
||||||
let mut any_run = true;
|
let mut any_run = true;
|
||||||
for _ in range(0, 5i) {
|
for _ in range(0, 5i) {
|
||||||
|
|
|
@ -26,8 +26,6 @@ use libc;
|
||||||
use ptr;
|
use ptr;
|
||||||
use comm;
|
use comm;
|
||||||
|
|
||||||
use sys::c;
|
|
||||||
use sys::fs::FileDesc;
|
|
||||||
use sys_common::helper_thread::Helper;
|
use sys_common::helper_thread::Helper;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use io::IoResult;
|
use io::IoResult;
|
||||||
|
@ -80,9 +78,10 @@ fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
|
||||||
None => {}
|
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) => {
|
Err(comm::Disconnected) => {
|
||||||
assert_eq!(objs.len(), 1);
|
|
||||||
assert_eq!(chans.len(), 0);
|
|
||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
Err(..) => break
|
Err(..) => break
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
//! to working in raw UTF-16, with such a wrapper around it.
|
//! to working in raw UTF-16, with such a wrapper around it.
|
||||||
|
|
||||||
use super::c::{ReadConsoleW, WriteConsoleW, GetConsoleMode, SetConsoleMode};
|
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_ECHO_INPUT, ENABLE_EXTENDED_FLAGS};
|
||||||
use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
|
use super::c::{ENABLE_INSERT_MODE, ENABLE_LINE_INPUT};
|
||||||
use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
|
use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
|
||||||
|
@ -38,6 +37,8 @@ use prelude::*;
|
||||||
use ptr;
|
use ptr;
|
||||||
use str::from_utf8;
|
use str::from_utf8;
|
||||||
|
|
||||||
|
use sys_common::unimpl;
|
||||||
|
|
||||||
fn invalid_encoding() -> IoError {
|
fn invalid_encoding() -> IoError {
|
||||||
IoError {
|
IoError {
|
||||||
kind: io::InvalidInput,
|
kind: io::InvalidInput,
|
||||||
|
@ -149,11 +150,8 @@ impl TTY {
|
||||||
// Make a CONSOLE_SCREEN_BUFFER_INFO
|
// Make a CONSOLE_SCREEN_BUFFER_INFO
|
||||||
// Call GetConsoleScreenBufferInfo
|
// Call GetConsoleScreenBufferInfo
|
||||||
// Maybe call GetLargestConsoleWindowSize instead?
|
// 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 {
|
impl Drop for TTY {
|
||||||
|
|
|
@ -232,13 +232,10 @@ impl Builder {
|
||||||
let my_stack_top = addr as uint;
|
let my_stack_top = addr as uint;
|
||||||
let my_stack_bottom = my_stack_top - stack_size + 1024;
|
let my_stack_bottom = my_stack_top - stack_size + 1024;
|
||||||
unsafe {
|
unsafe {
|
||||||
stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top);
|
stack::record_os_managed_stack_bounds(my_stack_bottom,
|
||||||
|
my_stack_top);
|
||||||
|
thread_info::set(imp::guard::current(), their_thread);
|
||||||
}
|
}
|
||||||
thread_info::set(
|
|
||||||
(my_stack_bottom, my_stack_top),
|
|
||||||
unsafe { imp::guard::current() },
|
|
||||||
their_thread
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut output = None;
|
let mut output = None;
|
||||||
let f: Thunk<(), T> = if stdout.is_some() || stderr.is_some() {
|
let f: Thunk<(), T> = if stdout.is_some() || stderr.is_some() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue