mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 19:17:43 +00:00
Revert "std: Re-enable at_exit()"
This reverts commit 9e224c2bf1
.
Conflicts:
src/libstd/sys/windows/os.rs
This commit is contained in:
parent
582cba183f
commit
aec67c2ee0
@ -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, StaticMutex, MUTEX_INIT};
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
@ -193,8 +193,6 @@ 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,18 +279,9 @@ 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.
|
||||||
unsafe {
|
match unsafe { FILTER.as_ref() } {
|
||||||
let _g = LOCK.lock();
|
Some(filter) if !filter.is_match(args.to_string()[]) => return,
|
||||||
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
|
||||||
@ -374,15 +363,9 @@ 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() });
|
||||||
|
|
||||||
let _g = LOCK.lock();
|
enabled(level, module, unsafe { (*DIRECTIVES).iter() })
|
||||||
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,
|
||||||
@ -438,15 +421,14 @@ 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 = 1 as *const Vec<directive::LogDirective>;
|
DIRECTIVES = 0 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 = 1 as *const _;
|
FILTER = 0 as *const _;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,19 +26,29 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
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::{IoResult, IoError, OtherIoError};
|
use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
|
||||||
use io::{standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
|
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, StaticMutex, MUTEX_INIT};
|
use sync::{Arc, Mutex, MutexGuard, Once, ONCE_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
|
||||||
@ -205,15 +215,14 @@ 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 LOCK: StaticMutex = MUTEX_INIT;
|
static ONCE: Once = ONCE_INIT;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _g = LOCK.lock();
|
ONCE.doit(|| {
|
||||||
if STDIN as uint == 0 {
|
// The default buffer capacity is 64k, but apparently windows doesn't like
|
||||||
// The default buffer capacity is 64k, but apparently windows
|
// 64k reads on stdin. See #13304 for details, but the idea is that on
|
||||||
// doesn't like 64k reads on stdin. See #13304 for details, but the
|
// windows we use a slightly smaller buffer that's been seen to be
|
||||||
// idea is that on windows we use a slightly smaller buffer that's
|
// acceptable.
|
||||||
// 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 {
|
||||||
@ -226,15 +235,11 @@ pub fn stdin() -> StdinReader {
|
|||||||
|
|
||||||
// Make sure to free it at exit
|
// Make sure to free it at exit
|
||||||
rt::at_exit(|| {
|
rt::at_exit(|| {
|
||||||
let g = LOCK.lock();
|
mem::transmute::<_, Box<StdinReader>>(STDIN);
|
||||||
let stdin = STDIN;
|
STDIN = 0 as *const _;
|
||||||
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,13 +229,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,8 +29,6 @@ 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();
|
||||||
@ -51,7 +49,7 @@ pub fn cleanup() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
let queue = QUEUE;
|
let queue = QUEUE;
|
||||||
QUEUE = 1u as *mut _;
|
QUEUE = 1 as *mut _;
|
||||||
LOCK.unlock();
|
LOCK.unlock();
|
||||||
|
|
||||||
// make sure we're not recursively cleaning up
|
// make sure we're not recursively cleaning up
|
||||||
|
@ -92,7 +92,9 @@ 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(sys::thread::guard::main(), thread);
|
thread_info::set((my_stack_bottom, my_stack_top),
|
||||||
|
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
|
||||||
@ -131,13 +133,19 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enqueues a procedure to run when the main thread exits.
|
/// 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.
|
||||||
///
|
///
|
||||||
/// 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));
|
||||||
}
|
}
|
||||||
@ -154,5 +162,8 @@ 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();
|
||||||
at_exit_imp::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();
|
||||||
}
|
}
|
||||||
|
@ -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 sys_common::mutex::{Mutex, MUTEX_INIT};
|
use sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
use rt::libunwind as uw;
|
use rt::libunwind as uw;
|
||||||
|
|
||||||
@ -540,20 +540,11 @@ 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>,
|
fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) -> ! {
|
||||||
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.
|
||||||
unsafe {
|
static INIT: Once = ONCE_INIT;
|
||||||
static LOCK: Mutex = MUTEX_INIT;
|
INIT.doit(|| unsafe { register(failure::on_fail); });
|
||||||
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,8 +20,6 @@
|
|||||||
//! 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;
|
||||||
@ -70,17 +68,6 @@ 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.
|
||||||
@ -97,7 +84,7 @@ impl<M: Send> Helper<M> {
|
|||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let _guard = self.lock.lock().unwrap();
|
let _guard = self.lock.lock().unwrap();
|
||||||
if *self.chan.get() as uint == 0 {
|
if !*self.initialized.get() {
|
||||||
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();
|
||||||
@ -106,17 +93,15 @@ 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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,9 +116,7 @@ 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() as uint != 0);
|
assert!(!self.chan.get().is_null());
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -146,13 +129,9 @@ 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() = 1 as *mut Sender<M>;
|
*self.chan.get() = 0 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)]
|
||||||
#![macro_escape]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use io::{mod, IoError, IoResult};
|
use io::{mod, IoError, IoResult};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
@ -29,7 +29,6 @@ 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.
|
||||||
|
@ -23,9 +23,7 @@ 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;
|
use sync::{Mutex, MutexGuard};
|
||||||
#[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;
|
||||||
@ -613,13 +611,11 @@ 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,7 +26,6 @@ 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,6 +121,37 @@ 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,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.
|
||||||
|
|
||||||
#![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;
|
||||||
@ -17,6 +15,10 @@ 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,
|
||||||
}
|
}
|
||||||
@ -33,6 +35,7 @@ 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),
|
||||||
})
|
})
|
||||||
@ -50,9 +53,10 @@ pub fn stack_guard() -> uint {
|
|||||||
ThreadInfo::with(|info| info.stack_guard)
|
ThreadInfo::with(|info| info.stack_guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(stack_guard: uint, thread: Thread) {
|
pub fn set(stack_bounds: (uint, uint), 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,6 +140,9 @@ 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;
|
use io::{IoResult, Writer};
|
||||||
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 = LOCK.lock();
|
let _g = unsafe { LOCK.lock() };
|
||||||
|
|
||||||
try!(writeln!(w, "stack backtrace:"));
|
try!(writeln!(w, "stack backtrace:"));
|
||||||
|
|
||||||
@ -241,8 +241,12 @@ 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,14 +10,30 @@
|
|||||||
|
|
||||||
#![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,6 +11,7 @@
|
|||||||
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> }
|
||||||
|
|
||||||
@ -25,7 +26,6 @@ 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,16 +10,17 @@
|
|||||||
|
|
||||||
//! 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};
|
use libc::{mod, c_int, c_char, c_void};
|
||||||
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: self.inner.lock.lock().unwrap(),
|
guard: unsafe { 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::{IoResult, EndOfFile};
|
use io::{mod, IoResult, IoError, 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.
|
||||||
|
|
||||||
HELPER.boot(register_sigchld, waitpid_helper);
|
unsafe { 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();
|
||||||
HELPER.send(NewChild(self.pid, tx, deadline));
|
unsafe { 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,15 +419,8 @@ 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,7 +17,6 @@ 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,6 +34,7 @@ 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,6 +135,10 @@ 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 fd = FileDesc::new(input, true);
|
let mut 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,15 +168,8 @@ 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,4 +43,5 @@ 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,22 +7,19 @@
|
|||||||
// <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
|
||||||
//! As always, windows has something very different than unix, we mainly want
|
/// to avoid having to depend too much on libunwind for windows.
|
||||||
//! 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
|
||||||
//! 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
|
||||||
//! 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
|
||||||
//! 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
|
||||||
//! via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent
|
/// of it.
|
||||||
//! of it.
|
///
|
||||||
//!
|
/// You'll also find that there's a function called CaptureStackBackTrace
|
||||||
//! 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
|
||||||
//! 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,
|
||||||
//! 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.
|
||||||
//! 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;
|
||||||
@ -297,7 +294,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 = LOCK.lock();
|
let _g = unsafe { 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,6 +15,7 @@
|
|||||||
#![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,17 +10,21 @@
|
|||||||
|
|
||||||
//! 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 io;
|
use c_str::CString;
|
||||||
use mem;
|
use mem;
|
||||||
use ptr;
|
|
||||||
use sys::os::fill_utf16_buf_and_decode;
|
use sys::os::fill_utf16_buf_and_decode;
|
||||||
|
use path;
|
||||||
|
use ptr;
|
||||||
|
use str;
|
||||||
|
use io;
|
||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use sys;
|
use sys;
|
||||||
use sys::os;
|
use sys::os;
|
||||||
use sys_common::{unimpl, mkerr_libc};
|
use sys_common::{keep_going, eof, 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};
|
||||||
@ -441,7 +445,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(unimpl())
|
Err(super::unimpl())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
|
||||||
|
@ -11,14 +11,30 @@
|
|||||||
#![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;
|
||||||
@ -164,6 +180,14 @@ 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,6 +15,7 @@
|
|||||||
|
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
|
use fmt;
|
||||||
use io::{IoResult, IoError};
|
use io::{IoResult, IoError};
|
||||||
use iter::repeat;
|
use iter::repeat;
|
||||||
use libc::{c_int, c_void};
|
use libc::{c_int, c_void};
|
||||||
|
@ -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 = self.inner.lock.lock();
|
let guard = unsafe { 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 = self.inner.lock.lock();
|
let guard = unsafe { 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 = self.inner.lock.lock();
|
let _guard = unsafe { 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 = self.inner.lock.lock();
|
let _guard = unsafe { 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,24 +8,25 @@
|
|||||||
// 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 prelude::*;
|
use libc::{pid_t, c_void, c_int};
|
||||||
|
|
||||||
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 io::process::{ProcessExit, ExitStatus};
|
use prelude::*;
|
||||||
|
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::timer;
|
use sys::fs;
|
||||||
|
use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
|
||||||
use sys::fs::FileDesc;
|
use sys::fs::FileDesc;
|
||||||
use sys_common::{AsInner, timeout};
|
use sys_common::helper_thread::Helper;
|
||||||
|
use sys_common::{AsInner, mkerr_libc, timeout};
|
||||||
|
|
||||||
use io::fs::PathExtensions;
|
use io::fs::PathExtensions;
|
||||||
|
|
||||||
@ -120,6 +121,8 @@ 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;
|
use sys_common::{stack, thread_info};
|
||||||
|
|
||||||
pub struct Handler {
|
pub struct Handler {
|
||||||
_data: *mut libc::c_void
|
_data: *mut libc::c_void
|
||||||
@ -30,6 +30,14 @@ 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,10 +14,11 @@ use libc;
|
|||||||
use mem;
|
use mem;
|
||||||
use ptr;
|
use ptr;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use super::{last_error, last_net_error, sock_t};
|
use super::{last_error, last_net_error, retry, 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::{timeout, eof, net};
|
use sys_common::{mod, timeout, eof, net};
|
||||||
|
|
||||||
pub use sys_common::net::TcpStream;
|
pub use sys_common::net::TcpStream;
|
||||||
|
|
||||||
@ -204,6 +205,10 @@ 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,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.
|
||||||
|
|
||||||
|
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 = 1 as *mut _;
|
DTORS = 0 as *mut _;
|
||||||
mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
|
mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors);
|
||||||
assert!(DTORS as uint == 1); // can't re-init after destructing
|
assert!(DTORS.is_null()); // can't re-init after destructing
|
||||||
DTOR_LOCK.unlock();
|
DTOR_LOCK.unlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -147,9 +147,6 @@ 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();
|
||||||
}
|
}
|
||||||
@ -157,9 +154,6 @@ 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();
|
||||||
@ -238,7 +232,6 @@ 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,6 +26,8 @@ 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;
|
||||||
@ -78,10 +80,9 @@ 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,6 +26,7 @@
|
|||||||
//! 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,8 +39,6 @@ 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,
|
||||||
@ -151,8 +150,11 @@ 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(unimpl())
|
Err(super::unimpl())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Let us magically declare this as a TTY
|
||||||
|
pub fn isatty(&self) -> bool { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for TTY {
|
impl Drop for TTY {
|
||||||
|
@ -232,10 +232,13 @@ 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,
|
stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top);
|
||||||
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…
Reference in New Issue
Block a user