1
Fork 0

std: Internalize almost all of std::rt

This commit does some refactoring to make almost all of the `std::rt` private.
Specifically, the following items are no longer part of its API:

* DEFAULT_ERROR_CODE
* backtrace
* unwind
* args
* at_exit
* cleanup
* heap (this is just alloc::heap)
* min_stack
* util

The module is now tagged as `#[doc(hidden)]` as the only purpose it's serve is
an entry point for the `panic!` macro via the `begin_unwind` and
`begin_unwind_fmt` reexports.
This commit is contained in:
Alex Crichton 2015-09-08 15:53:46 -07:00
parent 192c37537b
commit f4be2026df
33 changed files with 273 additions and 432 deletions

View file

@ -26,13 +26,16 @@ this is totally fine.
For instance, a custom implementation of `Box` might write `Drop` like this: For instance, a custom implementation of `Box` might write `Drop` like this:
```rust ```rust
#![feature(heap_api, core_intrinsics, unique)] #![feature(alloc, heap_api, core_intrinsics, unique)]
extern crate alloc;
use std::rt::heap;
use std::ptr::Unique; use std::ptr::Unique;
use std::intrinsics::drop_in_place; use std::intrinsics::drop_in_place;
use std::mem; use std::mem;
use alloc::heap;
struct Box<T>{ ptr: Unique<T> } struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
@ -45,6 +48,7 @@ impl<T> Drop for Box<T> {
} }
} }
} }
# fn main() {}
``` ```
and this works fine because when Rust goes to drop the `ptr` field it just sees and this works fine because when Rust goes to drop the `ptr` field it just sees
@ -54,13 +58,16 @@ use-after-free the `ptr` because when drop exits, it becomes inacessible.
However this wouldn't work: However this wouldn't work:
```rust ```rust
#![feature(heap_api, core_intrinsics, unique)] #![feature(alloc, heap_api, core_intrinsics, unique)]
extern crate alloc;
use std::rt::heap;
use std::ptr::Unique; use std::ptr::Unique;
use std::intrinsics::drop_in_place; use std::intrinsics::drop_in_place;
use std::mem; use std::mem;
use alloc::heap;
struct Box<T>{ ptr: Unique<T> } struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
@ -87,6 +94,7 @@ impl<T> Drop for SuperBox<T> {
} }
} }
} }
# fn main() {}
``` ```
After we deallocate the `box`'s ptr in SuperBox's destructor, Rust will After we deallocate the `box`'s ptr in SuperBox's destructor, Rust will
@ -129,13 +137,16 @@ The classic safe solution to overriding recursive drop and allowing moving out
of Self during `drop` is to use an Option: of Self during `drop` is to use an Option:
```rust ```rust
#![feature(heap_api, core_intrinsics, unique)] #![feature(alloc, heap_api, core_intrinsics, unique)]
extern crate alloc;
use std::rt::heap;
use std::ptr::Unique; use std::ptr::Unique;
use std::intrinsics::drop_in_place; use std::intrinsics::drop_in_place;
use std::mem; use std::mem;
use alloc::heap;
struct Box<T>{ ptr: Unique<T> } struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> { impl<T> Drop for Box<T> {
@ -165,6 +176,7 @@ impl<T> Drop for SuperBox<T> {
} }
} }
} }
# fn main() {}
``` ```
However this has fairly odd semantics: you're saying that a field that *should* However this has fairly odd semantics: you're saying that a field that *should*

View file

@ -9,7 +9,7 @@ This is perfectly fine because we already have `cap == 0` as our sentinel for no
allocation. We don't even need to handle it specially in almost any code because allocation. We don't even need to handle it specially in almost any code because
we usually need to check if `cap > len` or `len > 0` anyway. The traditional we usually need to check if `cap > len` or `len > 0` anyway. The traditional
Rust value to put here is `0x01`. The standard library actually exposes this Rust value to put here is `0x01`. The standard library actually exposes this
as `std::rt::heap::EMPTY`. There are quite a few places where we'll as `alloc::heap::EMPTY`. There are quite a few places where we'll
want to use `heap::EMPTY` because there's no real allocation to talk about but want to use `heap::EMPTY` because there's no real allocation to talk about but
`null` would make the compiler do bad things. `null` would make the compiler do bad things.
@ -20,11 +20,12 @@ the `heap` API anyway, so let's just get that dependency over with.
So: So:
```rust,ignore ```rust,ignore
#![feature(heap_api)] #![feature(alloc, heap_api)]
use std::rt::heap::EMPTY;
use std::mem; use std::mem;
use alloc::heap::EMPTY;
impl<T> Vec<T> { impl<T> Vec<T> {
fn new() -> Self { fn new() -> Self {
assert!(mem::size_of::<T>() != 0, "We're not ready to handle ZSTs"); assert!(mem::size_of::<T>() != 0, "We're not ready to handle ZSTs");

View file

@ -2,17 +2,16 @@
```rust ```rust
#![feature(unique)] #![feature(unique)]
#![feature(heap_api)] #![feature(alloc, heap_api)]
extern crate alloc;
use std::ptr::{Unique, self}; use std::ptr::{Unique, self};
use std::rt::heap;
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::marker::PhantomData; use std::marker::PhantomData;
use alloc::heap;
struct RawVec<T> { struct RawVec<T> {
ptr: Unique<T>, ptr: Unique<T>,

View file

@ -49,7 +49,8 @@ use std::marker;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use std::rt::heap::{allocate, deallocate};
use alloc::heap::{allocate, deallocate};
// The way arena uses arrays is really deeply awful. The arrays are // The way arena uses arrays is really deeply awful. The arrays are
// allocated, and have capacities reserved, but the fill for the array // allocated, and have capacities reserved, but the fill for the array

View file

@ -174,7 +174,6 @@
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(const_fn)] #![feature(const_fn)]
#![feature(iter_cmp)] #![feature(iter_cmp)]
#![feature(rt)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(static_mutex)] #![feature(static_mutex)]
@ -185,7 +184,6 @@ use std::io::prelude::*;
use std::mem; use std::mem;
use std::env; use std::env;
use std::ptr; use std::ptr;
use std::rt;
use std::slice; use std::slice;
use std::sync::{Once, StaticMutex}; use std::sync::{Once, StaticMutex};
@ -292,7 +290,6 @@ pub fn log(level: u32, loc: &'static LogLocation, args: fmt::Arguments) {
let _g = LOCK.lock(); let _g = LOCK.lock();
match FILTER as usize { match FILTER as usize {
0 => {} 0 => {}
1 => panic!("cannot log after main thread has exited"),
n => { n => {
let filter = mem::transmute::<_, &String>(n); let filter = mem::transmute::<_, &String>(n);
if !args.to_string().contains(filter) { if !args.to_string().contains(filter) {
@ -385,9 +382,6 @@ pub fn mod_enabled(level: u32, module: &str) -> bool {
let _g = LOCK.lock(); let _g = LOCK.lock();
unsafe { unsafe {
assert!(DIRECTIVES as usize != 0); assert!(DIRECTIVES as usize != 0);
assert!(DIRECTIVES as usize != 1,
"cannot log after the main thread has exited");
enabled(level, module, (*DIRECTIVES).iter()) enabled(level, module, (*DIRECTIVES).iter())
} }
} }
@ -442,19 +436,6 @@ fn init() {
assert!(DIRECTIVES.is_null()); assert!(DIRECTIVES.is_null());
DIRECTIVES = Box::into_raw(box directives); DIRECTIVES = Box::into_raw(box directives);
// Schedule the cleanup for the globals for when the runtime exits.
let _ = rt::at_exit(move || {
let _g = LOCK.lock();
assert!(!DIRECTIVES.is_null());
let _directives = Box::from_raw(DIRECTIVES);
DIRECTIVES = 1 as *mut _;
if !FILTER.is_null() {
let _filter = Box::from_raw(FILTER);
FILTER = 1 as *mut _;
}
});
} }
} }

View file

@ -8,23 +8,20 @@
// 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 self::BucketState::*; use alloc::heap::{allocate, deallocate, EMPTY};
use clone::Clone;
use cmp; use cmp;
use hash::{Hash, Hasher}; use hash::{Hash, Hasher};
use iter::{Iterator, ExactSizeIterator}; use marker;
use marker::{Copy, Send, Sync, Sized, self};
use mem::{align_of, size_of}; use mem::{align_of, size_of};
use mem; use mem;
use num::wrapping::OverflowingOps; use num::wrapping::OverflowingOps;
use ops::{Deref, DerefMut, Drop}; use ops::{Deref, DerefMut};
use option::Option;
use option::Option::{Some, None};
use ptr::{self, Unique}; use ptr::{self, Unique};
use rt::heap::{allocate, deallocate, EMPTY};
use collections::hash_state::HashState; use collections::hash_state::HashState;
use self::BucketState::*;
const EMPTY_BUCKET: u64 = 0; const EMPTY_BUCKET: u64 = 0;
/// The raw hashtable, providing safe-ish access to the unzipped and highly /// The raw hashtable, providing safe-ish access to the unzipped and highly

View file

@ -12,8 +12,8 @@ use prelude::v1::*;
use cell::Cell; use cell::Cell;
use ptr; use ptr;
use rt;
use sync::{StaticMutex, Arc}; use sync::{StaticMutex, Arc};
use sys_common;
pub struct Lazy<T> { pub struct Lazy<T> {
lock: StaticMutex, lock: StaticMutex,
@ -51,7 +51,7 @@ impl<T: Send + Sync + 'static> Lazy<T> {
// `Arc` allocation in our own internal box (it will get deallocated by // `Arc` allocation in our own internal box (it will get deallocated by
// the at exit handler). Otherwise we just return the freshly allocated // the at exit handler). Otherwise we just return the freshly allocated
// `Arc`. // `Arc`.
let registered = rt::at_exit(move || { let registered = sys_common::at_exit(move || {
let g = self.lock.lock(); let g = self.lock.lock();
let ptr = self.ptr.get(); let ptr = self.ptr.get();
self.ptr.set(1 as *mut _); self.ptr.set(1 as *mut _);

View file

@ -13,9 +13,10 @@ use io::prelude::*;
use any::Any; use any::Any;
use cell::RefCell; use cell::RefCell;
use rt::{backtrace, unwind};
use sys::stdio::Stderr; use sys::stdio::Stderr;
use sys_common::backtrace;
use sys_common::thread_info; use sys_common::thread_info;
use sys_common::unwind;
thread_local! { thread_local! {
pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = { pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = {

View file

@ -582,7 +582,7 @@ impl Child {
/// to run. /// to run.
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub fn exit(code: i32) -> ! { pub fn exit(code: i32) -> ! {
::rt::cleanup(); ::sys_common::cleanup();
::sys::os::exit(code) ::sys::os::exit(code)
} }

65
src/libstd/rt.rs Normal file
View file

@ -0,0 +1,65 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Runtime services
//!
//! The `rt` module provides a narrow set of runtime services,
//! including the global heap (exported in `heap`) and unwinding and
//! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.
#![unstable(feature = "rt",
reason = "this public module should not exist and is highly likely \
to disappear",
issue = "0")]
#![doc(hidden)]
use borrow::ToOwned;
use mem;
use sys;
use sys_common::thread_info::{self, NewThread};
use sys_common;
use thread::{self, Thread};
// Reexport some of our utilities which are expected by other crates.
pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt};
#[cfg(not(test))]
#[lang = "start"]
fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
sys::init();
let failed = unsafe {
let main_guard = sys::thread::guard::init();
sys::stack_overflow::init();
// Next, set up the current Thread with the guard information we just
// created. Note that this isn't necessary in general for new threads,
// but we just do this to name the main thread and to give it correct
// info about the stack bounds.
let thread: Thread = NewThread::new(Some("<main>".to_owned()));
thread_info::set(main_guard, thread);
// Store our args if necessary in a squirreled away location
sys_common::args::init(argc, argv);
// Let's run some code!
let res = thread::catch_panic(mem::transmute::<_, fn()>(main));
sys_common::cleanup();
res.is_err()
};
if failed {
101
} else {
0
}
}

View file

@ -1,76 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Simple backtrace functionality (to print on panic)
#![allow(non_camel_case_types)]
use env;
use sync::atomic::{self, Ordering};
pub use sys::backtrace::write;
// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
match ENABLED.load(Ordering::SeqCst) {
1 => return false,
2 => return true,
_ => {}
}
let val = match env::var_os("RUST_BACKTRACE") {
Some(..) => 2,
None => 1,
};
ENABLED.store(val, Ordering::SeqCst);
val == 2
}
#[cfg(test)]
mod tests {
use prelude::v1::*;
use sys_common;
macro_rules! t { ($a:expr, $b:expr) => ({
let mut m = Vec::new();
sys_common::backtrace::demangle(&mut m, $a).unwrap();
assert_eq!(String::from_utf8(m).unwrap(), $b);
}) }
#[test]
fn demangle() {
t!("test", "test");
t!("_ZN4testE", "test");
t!("_ZN4test", "_ZN4test");
t!("_ZN4test1a2bcE", "test::a::bc");
}
#[test]
fn demangle_dollars() {
t!("_ZN4$RP$E", ")");
t!("_ZN8$RF$testE", "&test");
t!("_ZN8$BP$test4foobE", "*test::foob");
t!("_ZN9$u20$test4foobE", " test::foob");
}
#[test]
fn demangle_many_dollars() {
t!("_ZN13test$u20$test4foobE", "test test::foob");
t!("_ZN12test$BP$test4foobE", "test*test::foob");
}
#[test]
fn demangle_windows() {
t!("ZN4testE", "test");
t!("ZN13test$u20$test4foobE", "test test::foob");
t!("ZN12test$RF$test4foobE", "test&test::foob");
}
}

View file

@ -1,51 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Macros used by the runtime.
//!
//! These macros call functions which are only accessible in the `rt` module, so
//! they aren't defined anywhere outside of the `rt` module.
macro_rules! rterrln {
($fmt:expr) => ( {
::rt::util::dumb_print(format_args!(concat!($fmt, "\n")))
} );
($fmt:expr, $($arg:expr),*) => ( {
::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg),*))
} )
}
// Some basic logging. Enabled by passing `--cfg rtdebug` to the libstd build.
macro_rules! rtdebug {
($arg:expr) => ( {
if cfg!(rtdebug) {
rterrln!($arg)
}
} );
($str:expr, $($arg:expr),*) => ( {
if cfg!(rtdebug) {
rterrln!($str, $($arg),*)
}
})
}
macro_rules! rtassert {
( $arg:expr ) => ( {
if ::rt::util::ENFORCE_SANITY {
if !$arg {
rtabort!(" assertion failed: {}", stringify!($arg));
}
}
} )
}
macro_rules! rtabort {
($($arg:tt)*) => (::rt::util::abort(format_args!($($arg)*)))
}

View file

@ -1,135 +0,0 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Runtime services
//!
//! The `rt` module provides a narrow set of runtime services,
//! including the global heap (exported in `heap`) and unwinding and
//! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.
#![unstable(feature = "rt",
reason = "this public module should not exist and is highly likely \
to disappear",
issue = "0")]
#![allow(missing_docs)]
use prelude::v1::*;
use sync::Once;
use sys;
use thread;
// Reexport some of our utilities which are expected by other crates.
pub use self::util::min_stack;
pub use self::unwind::{begin_unwind, begin_unwind_fmt};
// Reexport some functionality from liballoc.
pub use alloc::heap;
// Simple backtrace functionality (to print on panic)
pub mod backtrace;
// Internals
#[macro_use]
mod macros;
// These should be refactored/moved/made private over time
pub mod util;
pub mod unwind;
pub mod args;
mod at_exit_imp;
mod libunwind;
mod dwarf;
/// The default error code of the rust runtime if the main thread panics instead
/// of exiting cleanly.
pub const DEFAULT_ERROR_CODE: isize = 101;
#[cfg(not(test))]
#[lang = "start"]
fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
use prelude::v1::*;
use mem;
use rt;
use sys_common::thread_info::{self, NewThread};
use thread::Thread;
let failed = unsafe {
let main_guard = sys::thread::guard::init();
sys::stack_overflow::init();
// Next, set up the current Thread with the guard information we just
// created. Note that this isn't necessary in general for new threads,
// but we just do this to name the main thread and to give it correct
// info about the stack bounds.
let thread: Thread = NewThread::new(Some("<main>".to_owned()));
thread_info::set(main_guard, thread);
// By default, some platforms will send a *signal* when a EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
// handler, causing it to kill the program, which isn't exactly what we
// want!
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem.
#[cfg(windows)] fn ignore_sigpipe() {}
#[cfg(unix)] fn ignore_sigpipe() {
use libc;
use libc::funcs::posix01::signal::signal;
unsafe {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
}
}
ignore_sigpipe();
// Store our args if necessary in a squirreled away location
args::init(argc, argv);
// And finally, let's run some code!
let res = thread::catch_panic(mem::transmute::<_, fn()>(main));
cleanup();
res.is_err()
};
// If the exit code wasn't set, then the try block must have panicked.
if failed {
rt::DEFAULT_ERROR_CODE
} else {
0
}
}
/// Enqueues a procedure to run when the main thread exits.
///
/// Currently these closures are only run once the main *Rust* thread exits.
/// Once the `at_exit` handlers begin running, more may be enqueued, but not
/// infinitely so. Eventually a handler registration will be forced to fail.
///
/// Returns `Ok` if the handler was successfully registered, meaning that the
/// closure will be run once the main thread exits. Returns `Err` to indicate
/// that the closure could not be registered, meaning that it is not scheduled
/// to be run.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}
/// One-time runtime cleanup.
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
args::cleanup();
sys::stack_overflow::cleanup();
at_exit_imp::cleanup();
});
}

View file

@ -19,6 +19,8 @@
//! //!
//! FIXME #7756: Would be nice for this to not exist. //! FIXME #7756: Would be nice for this to not exist.
#![allow(dead_code)] // different code on OSX/linux/etc
use vec::Vec; use vec::Vec;
/// One-time global initialization. /// One-time global initialization.
@ -27,14 +29,6 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { imp::init(argc, argv)
/// One-time global cleanup. /// One-time global cleanup.
pub unsafe fn cleanup() { imp::cleanup() } pub unsafe fn cleanup() { imp::cleanup() }
/// Take the global arguments from global storage.
pub fn take() -> Option<Vec<Vec<u8>>> { imp::take() }
/// Give the global arguments to global storage.
///
/// It is an error if the arguments already exist.
pub fn put(args: Vec<Vec<u8>>) { imp::put(args) }
/// Make a clone of the global arguments. /// Make a clone of the global arguments.
pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() } pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() }
@ -48,7 +42,7 @@ pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() }
mod imp { mod imp {
use prelude::v1::*; use prelude::v1::*;
use libc; use libc::c_char;
use mem; use mem;
use ffi::CStr; use ffi::CStr;
@ -58,37 +52,26 @@ mod imp {
static LOCK: StaticMutex = StaticMutex::new(); static LOCK: StaticMutex = StaticMutex::new();
pub unsafe fn init(argc: isize, argv: *const *const u8) { pub unsafe fn init(argc: isize, argv: *const *const u8) {
let args = load_argc_and_argv(argc, argv); let args = (0..argc).map(|i| {
put(args); CStr::from_ptr(*argv.offset(i) as *const c_char).to_bytes().to_vec()
}).collect();
let _guard = LOCK.lock();
let ptr = get_global_ptr();
assert!((*ptr).is_none());
(*ptr) = Some(box args);
} }
pub unsafe fn cleanup() { pub unsafe fn cleanup() {
take();
}
pub fn take() -> Option<Vec<Vec<u8>>> {
let _guard = LOCK.lock(); let _guard = LOCK.lock();
unsafe { *get_global_ptr() = None;
let ptr = get_global_ptr();
let val = mem::replace(&mut *ptr, None);
val.as_ref().map(|s: &Box<Vec<Vec<u8>>>| (**s).clone())
}
}
pub fn put(args: Vec<Vec<u8>>) {
let _guard = LOCK.lock();
unsafe {
let ptr = get_global_ptr();
rtassert!((*ptr).is_none());
(*ptr) = Some(box args.clone());
}
} }
pub fn clone() -> Option<Vec<Vec<u8>>> { pub fn clone() -> Option<Vec<Vec<u8>>> {
let _guard = LOCK.lock(); let _guard = LOCK.lock();
unsafe { unsafe {
let ptr = get_global_ptr(); let ptr = get_global_ptr();
(*ptr).as_ref().map(|s: &Box<Vec<Vec<u8>>>| (**s).clone()) (*ptr).as_ref().map(|s| (**s).clone())
} }
} }
@ -96,42 +79,6 @@ mod imp {
unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } unsafe { mem::transmute(&GLOBAL_ARGS_PTR) }
} }
unsafe fn load_argc_and_argv(argc: isize,
argv: *const *const u8) -> Vec<Vec<u8>> {
let argv = argv as *const *const libc::c_char;
(0..argc).map(|i| {
CStr::from_ptr(*argv.offset(i)).to_bytes().to_vec()
}).collect()
}
#[cfg(test)]
mod tests {
use prelude::v1::*;
use super::*;
#[test]
fn smoke_test() {
// Preserve the actual global state.
let saved_value = take();
let expected = vec![
b"happy".to_vec(),
b"today?".to_vec(),
];
put(expected.clone());
assert!(clone() == Some(expected.clone()));
assert!(take() == Some(expected.clone()));
assert!(take() == None);
// Restore the actual global state.
match saved_value {
Some(ref args) => put(args.clone()),
None => ()
}
}
}
} }
#[cfg(any(target_os = "macos", #[cfg(any(target_os = "macos",
@ -146,14 +93,6 @@ mod imp {
pub fn cleanup() { pub fn cleanup() {
} }
pub fn take() -> Option<Vec<Vec<u8>>> {
panic!()
}
pub fn put(_args: Vec<Vec<u8>>) {
panic!()
}
pub fn clone() -> Option<Vec<Vec<u8>>> { pub fn clone() -> Option<Vec<Vec<u8>>> {
panic!() panic!()
} }

View file

@ -54,7 +54,7 @@ pub fn cleanup() {
LOCK.unlock(); LOCK.unlock();
// make sure we're not recursively cleaning up // make sure we're not recursively cleaning up
rtassert!(queue as usize != 1); assert!(queue as usize != 1);
// If we never called init, not need to cleanup! // If we never called init, not need to cleanup!
if queue as usize != 0 { if queue as usize != 0 {

View file

@ -8,10 +8,14 @@
// 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 io; use env;
use io::prelude::*; use io::prelude::*;
use str; use io;
use libc; use libc;
use str;
use sync::atomic::{self, Ordering};
pub use sys::backtrace::write;
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
pub const HEX_WIDTH: usize = 18; pub const HEX_WIDTH: usize = 18;
@ -19,6 +23,23 @@ pub const HEX_WIDTH: usize = 18;
#[cfg(target_pointer_width = "32")] #[cfg(target_pointer_width = "32")]
pub const HEX_WIDTH: usize = 10; pub const HEX_WIDTH: usize = 10;
// For now logging is turned off by default, and this function checks to see
// whether the magical environment variable is present to see if it's turned on.
pub fn log_enabled() -> bool {
static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
match ENABLED.load(Ordering::SeqCst) {
1 => return false,
2 => return true,
_ => {}
}
let val = match env::var_os("RUST_BACKTRACE") {
Some(..) => 2,
None => 1,
};
ENABLED.store(val, Ordering::SeqCst);
val == 2
}
// These output functions should now be used everywhere to ensure consistency. // These output functions should now be used everywhere to ensure consistency.
pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
@ -163,3 +184,43 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
Ok(()) Ok(())
} }
#[cfg(test)]
mod tests {
use prelude::v1::*;
use sys_common;
macro_rules! t { ($a:expr, $b:expr) => ({
let mut m = Vec::new();
sys_common::backtrace::demangle(&mut m, $a).unwrap();
assert_eq!(String::from_utf8(m).unwrap(), $b);
}) }
#[test]
fn demangle() {
t!("test", "test");
t!("_ZN4testE", "test");
t!("_ZN4test", "_ZN4test");
t!("_ZN4test1a2bcE", "test::a::bc");
}
#[test]
fn demangle_dollars() {
t!("_ZN4$RP$E", ")");
t!("_ZN8$RF$testE", "&test");
t!("_ZN8$BP$test4foobE", "*test::foob");
t!("_ZN9$u20$test4foobE", " test::foob");
}
#[test]
fn demangle_many_dollars() {
t!("_ZN13test$u20$test4foobE", "test test::foob");
t!("_ZN12test$BP$test4foobE", "test*test::foob");
}
#[test]
fn demangle_windows() {
t!("ZN4testE", "test");
t!("ZN13test$u20$test4foobE", "test test::foob");
t!("ZN12test$RF$test4foobE", "test&test::foob");
}
}

View file

@ -22,7 +22,7 @@
#![allow(unused)] #![allow(unused)]
use prelude::v1::*; use prelude::v1::*;
use rt::dwarf::DwarfReader; use sys_common::dwarf::DwarfReader;
use core::mem; use core::mem;
pub const DW_EH_PE_omit : u8 = 0xFF; pub const DW_EH_PE_omit : u8 = 0xFF;

View file

@ -10,17 +10,39 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use boxed::Box;
use sync::Once;
use sys;
macro_rules! rtabort {
($($t:tt)*) => (::sys_common::util::abort(format_args!($($t)*)))
}
macro_rules! rtassert {
($e:expr) => ({
if !$e {
rtabort!(concat!("assertion failed: ", stringify!($e)))
}
})
}
pub mod args;
pub mod at_exit_imp;
pub mod backtrace; pub mod backtrace;
pub mod condvar; pub mod condvar;
pub mod dwarf;
pub mod io;
pub mod libunwind;
pub mod mutex; pub mod mutex;
pub mod net; pub mod net;
pub mod io;
pub mod poison; pub mod poison;
pub mod remutex; pub mod remutex;
pub mod rwlock; pub mod rwlock;
pub mod thread; pub mod thread;
pub mod thread_info; pub mod thread_info;
pub mod thread_local; pub mod thread_local;
pub mod unwind;
pub mod util;
pub mod wtf8; pub mod wtf8;
#[cfg(any(all(unix, not(any(target_os = "macos", target_os = "ios"))), #[cfg(any(all(unix, not(any(target_os = "macos", target_os = "ios"))),
@ -52,3 +74,27 @@ pub trait IntoInner<Inner> {
pub trait FromInner<Inner> { pub trait FromInner<Inner> {
fn from_inner(inner: Inner) -> Self; fn from_inner(inner: Inner) -> Self;
} }
/// Enqueues a procedure to run when the main thread exits.
///
/// Currently these closures are only run once the main *Rust* thread exits.
/// Once the `at_exit` handlers begin running, more may be enqueued, but not
/// infinitely so. Eventually a handler registration will be forced to fail.
///
/// Returns `Ok` if the handler was successfully registered, meaning that the
/// closure will be run once the main thread exits. Returns `Err` to indicate
/// that the closure could not be registered, meaning that it is not scheduled
/// to be run.
pub fn at_exit<F: FnOnce() + Send + 'static>(f: F) -> Result<(), ()> {
if at_exit_imp::push(Box::new(f)) {Ok(())} else {Err(())}
}
/// One-time runtime cleanup.
pub fn cleanup() {
static CLEANUP: Once = Once::new();
CLEANUP.call_once(|| unsafe {
args::cleanup();
sys::stack_overflow::cleanup();
at_exit_imp::cleanup();
});
}

View file

@ -13,7 +13,7 @@
use prelude::v1::*; use prelude::v1::*;
use any::Any; use any::Any;
use rt::libunwind as uw; use sys_common::libunwind as uw;
struct Exception { struct Exception {
uwe: uw::_Unwind_Exception, uwe: uw::_Unwind_Exception,
@ -35,7 +35,6 @@ pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
exception: *mut uw::_Unwind_Exception) { exception: *mut uw::_Unwind_Exception) {
rtdebug!("exception_cleanup()");
unsafe { unsafe {
let _: Box<Exception> = Box::from_raw(exception as *mut Exception); let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
} }
@ -44,7 +43,6 @@ pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
let my_ep = ptr as *mut Exception; let my_ep = ptr as *mut Exception;
rtdebug!("caught {}", (*my_ep).uwe.exception_class);
let cause = (*my_ep).cause.take(); let cause = (*my_ep).cause.take();
uw::_Unwind_DeleteException(ptr as *mut _); uw::_Unwind_DeleteException(ptr as *mut _);
cause.unwrap() cause.unwrap()
@ -80,7 +78,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
not(all(windows, target_arch = "x86_64")), not(all(windows, target_arch = "x86_64")),
not(test)))] not(test)))]
pub mod eabi { pub mod eabi {
use rt::libunwind as uw; use sys_common::libunwind as uw;
use libc::c_int; use libc::c_int;
extern { extern {
@ -136,7 +134,7 @@ pub mod eabi {
#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] #[cfg(all(target_os = "ios", target_arch = "arm", not(test)))]
pub mod eabi { pub mod eabi {
use rt::libunwind as uw; use sys_common::libunwind as uw;
use libc::c_int; use libc::c_int;
extern { extern {
@ -191,7 +189,7 @@ pub mod eabi {
// but otherwise works the same. // but otherwise works the same.
#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))]
pub mod eabi { pub mod eabi {
use rt::libunwind as uw; use sys_common::libunwind as uw;
use libc::c_int; use libc::c_int;
extern { extern {

View file

@ -184,7 +184,6 @@ pub fn panicking() -> bool {
#[no_mangle] #[no_mangle]
#[allow(private_no_mangle_fns)] #[allow(private_no_mangle_fns)]
fn rust_panic(cause: Box<Any + Send + 'static>) -> ! { fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
rtdebug!("begin_unwind()");
unsafe { unsafe {
imp::panic(cause) imp::panic(cause)
} }
@ -288,7 +287,8 @@ fn begin_unwind_inner(msg: Box<Any + Send>,
// have limited options. Currently our preference is to // have limited options. Currently our preference is to
// just abort. In the future we may consider resuming // just abort. In the future we may consider resuming
// unwinding or otherwise exiting the thread cleanly. // unwinding or otherwise exiting the thread cleanly.
rterrln!("thread panicked while panicking. aborting."); super::util::dumb_print(format_args!("thread panicked while panicking. \
aborting."));
unsafe { intrinsics::abort() } unsafe { intrinsics::abort() }
} }
PANICKING.with(|s| s.set(true)); PANICKING.with(|s| s.set(true));

View file

@ -135,10 +135,11 @@ fn rust_eh_personality() {
// This function just takes a look at the current EXCEPTION_RECORD being thrown // This function just takes a look at the current EXCEPTION_RECORD being thrown
// to ensure that it's code is RUST_PANIC, which was set by the call to // to ensure that it's code is RUST_PANIC, which was set by the call to
// `RaiseException` above in the `panic` function. // `RaiseException` above in the `panic` function.
#[no_mangle]
#[lang = "msvc_try_filter"] #[lang = "msvc_try_filter"]
pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, #[linkage = "external"]
_rbp: *mut u8) -> i32 { #[allow(private_no_mangle_fns)]
extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS,
_rbp: *mut u8) -> i32 {
unsafe { unsafe {
((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
} }

View file

@ -18,7 +18,7 @@ use prelude::v1::*;
use any::Any; use any::Any;
use self::EXCEPTION_DISPOSITION::*; use self::EXCEPTION_DISPOSITION::*;
use rt::dwarf::eh; use sys_common::dwarf::eh;
use core::mem; use core::mem;
use core::ptr; use core::ptr;
use libc::{c_void, c_ulonglong, DWORD, LPVOID}; use libc::{c_void, c_ulonglong, DWORD, LPVOID};
@ -114,7 +114,6 @@ struct PanicData {
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
let panic_ctx = Box::new(PanicData { data: data }); let panic_ctx = Box::new(PanicData { data: data });
let params = [Box::into_raw(panic_ctx) as ULONG_PTR]; let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
rtdebug!("panic: ctx={:X}", params[0]);
RaiseException(RUST_PANIC, RaiseException(RUST_PANIC,
EXCEPTION_NONCONTINUABLE, EXCEPTION_NONCONTINUABLE,
params.len() as DWORD, params.len() as DWORD,
@ -123,7 +122,6 @@ pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
} }
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
rtdebug!("cleanup: ctx={:X}", ptr as usize);
let panic_ctx = Box::from_raw(ptr as *mut PanicData); let panic_ctx = Box::from_raw(ptr as *mut PanicData);
return panic_ctx.data; return panic_ctx.data;
} }
@ -174,15 +172,10 @@ unsafe extern fn rust_eh_personality(
{ {
let er = &*exceptionRecord; let er = &*exceptionRecord;
let dc = &*dispatcherContext; let dc = &*dispatcherContext;
rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}",
er.ExceptionCode, er.ExceptionFlags,
establisherFrame as usize, dc.ControlPc as usize);
if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
if er.ExceptionCode == RUST_PANIC { if er.ExceptionCode == RUST_PANIC {
if let Some(lpad) = find_landing_pad(dc) { if let Some(lpad) = find_landing_pad(dc) {
rtdebug!("unwinding to landing pad {:X}", lpad);
RtlUnwindEx(establisherFrame, RtlUnwindEx(establisherFrame,
lpad as LPVOID, lpad as LPVOID,
exceptionRecord, exceptionRecord,
@ -206,7 +199,6 @@ unsafe extern fn rust_eh_personality(
#[lang = "eh_unwind_resume"] #[lang = "eh_unwind_resume"]
#[cfg(not(test))] #[cfg(not(test))]
unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) { unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize);
let params = [panic_ctx as ULONG_PTR]; let params = [panic_ctx as ULONG_PTR];
RaiseException(RUST_PANIC, RaiseException(RUST_PANIC,
EXCEPTION_NONCONTINUABLE, EXCEPTION_NONCONTINUABLE,

View file

@ -8,13 +8,13 @@
// 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 io::prelude::*;
use env; use env;
use fmt; use fmt;
use intrinsics; use intrinsics;
use io::prelude::*;
use sync::atomic::{self, Ordering}; use sync::atomic::{self, Ordering};
use sys::stdio::Stderr; use sys::stdio::Stderr;
use thread;
pub fn min_stack() -> usize { pub fn min_stack() -> usize {
static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0); static MIN: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
@ -30,24 +30,17 @@ pub fn min_stack() -> usize {
amt amt
} }
// Indicates whether we should perform expensive sanity checks, including rtassert!
//
// FIXME: Once the runtime matures remove the `true` below to turn off rtassert,
// etc.
pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) ||
cfg!(rtassert);
pub fn dumb_print(args: fmt::Arguments) { pub fn dumb_print(args: fmt::Arguments) {
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args));
} }
pub fn abort(args: fmt::Arguments) -> ! { pub fn abort(args: fmt::Arguments) -> ! {
rterrln!("fatal runtime error: {}", args); dumb_print(format_args!("fatal runtime error: {}", args));
unsafe { intrinsics::abort(); } unsafe { intrinsics::abort(); }
} }
#[allow(dead_code)] // stack overflow detection not enabled on all platforms
pub unsafe fn report_overflow() { pub unsafe fn report_overflow() {
use thread; dumb_print(format_args!("\nthread '{}' has overflowed its stack",
rterrln!("\nthread '{}' has overflowed its stack", thread::current().name().unwrap_or("<unknown>")));
thread::current().name().unwrap_or("<unknown>"));
} }

View file

@ -12,6 +12,7 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
use io::{self, ErrorKind}; use io::{self, ErrorKind};
use libc::funcs::posix01::signal::signal;
use libc; use libc;
use num::One; use num::One;
use ops::Neg; use ops::Neg;
@ -47,6 +48,19 @@ pub mod thread_local;
pub mod time; pub mod time;
pub mod stdio; pub mod stdio;
pub fn init() {
// By default, some platforms will send a *signal* when a EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
// handler, causing it to kill the program, which isn't exactly what we
// want!
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem.
unsafe {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
}
}
pub fn decode_error_kind(errno: i32) -> ErrorKind { pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int { match errno as libc::c_int {
libc::ECONNREFUSED => ErrorKind::ConnectionRefused, libc::ECONNREFUSED => ErrorKind::ConnectionRefused,

View file

@ -358,8 +358,8 @@ pub fn args() -> Args {
target_os = "netbsd", target_os = "netbsd",
target_os = "openbsd"))] target_os = "openbsd"))]
pub fn args() -> Args { pub fn args() -> Args {
use rt; use sys_common;
let bytes = rt::args::clone().unwrap_or(Vec::new()); let bytes = sys_common::args::clone().unwrap_or(Vec::new());
let v: Vec<OsString> = bytes.into_iter().map(|v| { let v: Vec<OsString> = bytes.into_iter().map(|v| {
OsStringExt::from_vec(v) OsStringExt::from_vec(v)
}).collect(); }).collect();

View file

@ -38,7 +38,7 @@ impl Drop for Handler {
target_os = "openbsd"))] target_os = "openbsd"))]
mod imp { mod imp {
use super::Handler; use super::Handler;
use rt::util::report_overflow; use sys_common::util::report_overflow;
use mem; use mem;
use ptr; use ptr;
use sys::c::{siginfo, sigaction, SIGBUS, SIG_DFL, use sys::c::{siginfo, sigaction, SIGBUS, SIG_DFL,

View file

@ -43,6 +43,8 @@ pub mod thread_local;
pub mod time; pub mod time;
pub mod stdio; pub mod stdio;
pub fn init() {}
pub fn decode_error_kind(errno: i32) -> ErrorKind { pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int { match errno as libc::c_int {
libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied, libc::ERROR_ACCESS_DENIED => ErrorKind::PermissionDenied,

View file

@ -16,11 +16,10 @@ use net::SocketAddr;
use num::One; use num::One;
use ops::Neg; use ops::Neg;
use ptr; use ptr;
use rt;
use sync::Once; use sync::Once;
use sys; use sys;
use sys::c; use sys::c;
use sys_common::{AsInner, FromInner, IntoInner}; use sys_common::{self, AsInner, FromInner, IntoInner};
use sys_common::net::{setsockopt, getsockopt}; use sys_common::net::{setsockopt, getsockopt};
use time::Duration; use time::Duration;
@ -39,7 +38,7 @@ pub fn init() {
&mut data); &mut data);
assert_eq!(ret, 0); assert_eq!(ret, 0);
let _ = rt::at_exit(|| { c::WSACleanup(); }); let _ = sys_common::at_exit(|| { c::WSACleanup(); });
}); });
} }

View file

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use libc::{self, LONG}; use libc::{self, LONG};
use rt::util::report_overflow; use sys_common::util::report_overflow;
use sys::c; use sys::c;
pub struct Handler; pub struct Handler;

View file

@ -13,7 +13,7 @@ use prelude::v1::*;
use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
use ptr; use ptr;
use rt; use sys_common;
use sys_common::mutex::Mutex; use sys_common::mutex::Mutex;
pub type Key = DWORD; pub type Key = DWORD;
@ -133,7 +133,7 @@ unsafe fn init_dtors() {
let dtors = box Vec::<(Key, Dtor)>::new(); let dtors = box Vec::<(Key, Dtor)>::new();
let res = rt::at_exit(move|| { let res = sys_common::at_exit(move|| {
DTOR_LOCK.lock(); DTOR_LOCK.lock();
let dtors = DTORS; let dtors = DTORS;
DTORS = 1 as *mut _; DTORS = 1 as *mut _;

View file

@ -167,10 +167,11 @@ use any::Any;
use cell::UnsafeCell; use cell::UnsafeCell;
use fmt; use fmt;
use io; use io;
use rt::{self, unwind};
use sync::{Mutex, Condvar, Arc}; use sync::{Mutex, Condvar, Arc};
use sys::thread as imp; use sys::thread as imp;
use sys_common::thread_info; use sys_common::thread_info;
use sys_common::unwind;
use sys_common::util;
use time::Duration; use time::Duration;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -260,7 +261,7 @@ impl Builder {
-> io::Result<JoinInner<T>> { -> io::Result<JoinInner<T>> {
let Builder { name, stack_size } = self; let Builder { name, stack_size } = self;
let stack_size = stack_size.unwrap_or(rt::min_stack()); let stack_size = stack_size.unwrap_or(util::min_stack());
let my_thread = Thread::new(name); let my_thread = Thread::new(name);
let their_thread = my_thread.clone(); let their_thread = my_thread.clone();
@ -383,7 +384,7 @@ pub fn catch_panic<F, R>(f: F) -> Result<R>
let mut result = None; let mut result = None;
unsafe { unsafe {
let result = &mut result; let result = &mut result;
try!(::rt::unwind::try(move || *result = Some(f()))) try!(unwind::try(move || *result = Some(f())))
} }
Ok(result.unwrap()) Ok(result.unwrap())
} }