std: Extract librustrt out of libstd

As part of the libstd facade efforts, this commit extracts the runtime interface
out of the standard library into a standalone crate, librustrt. This crate will
provide the following services:

* Definition of the rtio interface
* Definition of the Runtime interface
* Implementation of the Task structure
* Implementation of task-local-data
* Implementation of task failure via unwinding via libunwind
* Implementation of runtime initialization and shutdown
* Implementation of thread-local-storage for the local rust Task

Notably, this crate avoids the following services:

* Thread creation and destruction. The crate does not require the knowledge of
  an OS threading system, and as a result it seemed best to leave out the
  `rt::thread` module from librustrt. The librustrt module does depend on
  mutexes, however.
* Implementation of backtraces. There is no inherent requirement for the runtime
  to be able to generate backtraces. As will be discussed later, this
  functionality continues to live in libstd rather than librustrt.

As usual, a number of architectural changes were required to make this crate
possible. Users of "stable" functionality will not be impacted by this change,
but users of the `std::rt` module will likely note the changes. A list of
architectural changes made is:

* The stdout/stderr handles no longer live directly inside of the `Task`
  structure. This is a consequence of librustrt not knowing about `std::io`.
  These two handles are now stored inside of task-local-data.

  The handles were originally stored inside of the `Task` for perf reasons, and
  TLD is not currently as fast as it could be. For comparison, 100k prints goes
  from 59ms to 68ms (a 15% slowdown). This appeared to me to be an acceptable
  perf loss for the successful extraction of a librustrt crate.

* The `rtio` module was forced to duplicate more functionality of `std::io`. As
  the module no longer depends on `std::io`, `rtio` now defines structures such
  as socket addresses, addrinfo fiddly bits, etc. The primary change made was
  that `rtio` now defines its own `IoError` type. This type is distinct from
  `std::io::IoError` in that it does not have an enum for what error occurred,
  but rather a platform-specific error code.

  The native and green libraries will be updated in later commits for this
  change, and the bulk of this effort was put behind updating the two libraries
  for this change (with `rtio`).

* Printing a message on task failure (along with the backtrace) continues to
  live in libstd, not in librustrt. This is a consequence of the above decision
  to move the stdout/stderr handles to TLD rather than inside the `Task` itself.
  The unwinding API now supports registration of global callback functions which
  will be invoked when a task fails, allowing for libstd to register a function
  to print a message and a backtrace.

  The API for registering a callback is experimental and unsafe, as the
  ramifications of running code on unwinding is pretty hairy.

* The `std::unstable::mutex` module has moved to `std::rt::mutex`.

* The `std::unstable::sync` module has been moved to `std::rt::exclusive` and
  the type has been rewritten to not internally have an Arc and to have an RAII
  guard structure when locking. Old code should stop using `Exclusive` in favor
  of the primitives in `libsync`, but if necessary, old code should port to
  `Arc<Exclusive<T>>`.

* The local heap has been stripped down to have fewer debugging options. None of
  these were tested, and none of these have been used in a very long time.

[breaking-change]
This commit is contained in:
Alex Crichton 2014-06-03 19:11:49 -07:00
parent a3f9aa9ef8
commit 5ec36c358f
30 changed files with 1302 additions and 1255 deletions

View File

@ -51,7 +51,7 @@
TARGET_CRATES := libc std green rustuv native flate arena glob term semver \
uuid serialize sync getopts collections num test time rand \
url log regex graphviz core rlibc alloc debug
url log regex graphviz core rlibc alloc debug rustrt
HOST_CRATES := syntax rustc rustdoc fourcc hexfloat regex_macros fmt_macros
CRATES := $(TARGET_CRATES) $(HOST_CRATES)
TOOLS := compiletest rustdoc rustc
@ -60,7 +60,9 @@ DEPS_core :=
DEPS_rlibc :=
DEPS_alloc := core libc native:jemalloc
DEPS_debug := std
DEPS_std := core rand libc alloc collections native:rustrt native:backtrace
DEPS_rustrt := alloc core libc collections native:rustrt_native
DEPS_std := core libc rand alloc collections rustrt \
native:rust_builtin native:backtrace
DEPS_graphviz := std
DEPS_green := std native:context_switch
DEPS_rustuv := std native:uv native:uv_support

View File

@ -35,8 +35,8 @@
# that's per-target so you're allowed to conditionally add files based on the
# target.
################################################################################
NATIVE_LIBS := rustrt hoedown uv_support morestack miniz context_switch \
rust_test_helpers
NATIVE_LIBS := rust_builtin hoedown uv_support morestack miniz context_switch \
rustrt_native rust_test_helpers
# $(1) is the target triple
define NATIVE_LIBRARIES
@ -52,8 +52,9 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \
hoedown/src/version.c
NATIVE_DEPS_uv_support_$(1) := rust_uv.c
NATIVE_DEPS_miniz_$(1) = miniz.c
NATIVE_DEPS_rustrt_$(1) := rust_builtin.c \
rust_android_dummy.c \
NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
rust_android_dummy.c
NATIVE_DEPS_rustrt_native_$(1) := \
rust_try.ll \
arch/$$(HOST_$(1))/record_sp.S
NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c

View File

@ -98,6 +98,20 @@ macro_rules! try(
($e:expr) => (match $e { Ok(e) => e, Err(e) => return Err(e) })
)
/// Writing a formatted string into a writer
#[macro_export]
macro_rules! write(
($dst:expr, $($arg:tt)*) => (format_args_method!($dst, write_fmt, $($arg)*))
)
/// Writing a formatted string plus a newline into a writer
#[macro_export]
macro_rules! writeln(
($dst:expr, $fmt:expr $($arg:tt)*) => (
write!($dst, concat!($fmt, "\n") $($arg)*)
)
)
#[cfg(test)]
macro_rules! vec( ($($e:expr),*) => ({
let mut _v = ::std::vec::Vec::new();

View File

@ -18,10 +18,9 @@
//! discover the command line arguments.
//!
//! FIXME #7756: Would be nice for this to not exist.
//! FIXME #7756: This has a lot of C glue for lack of globals.
use option::Option;
use vec::Vec;
use core::prelude::*;
use collections::vec::Vec;
/// One-time global initialization.
pub unsafe fn init(argc: int, argv: **u8) { imp::init(argc, argv) }
@ -44,14 +43,14 @@ pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() }
#[cfg(target_os = "android")]
#[cfg(target_os = "freebsd")]
mod imp {
use clone::Clone;
use iter::Iterator;
use option::{Option, Some, None};
use owned::Box;
use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use mem;
use vec::Vec;
use ptr::RawPtr;
use core::prelude::*;
use alloc::owned::Box;
use collections::vec::Vec;
use core::mem;
use core::slice;
use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
static mut global_args_ptr: uint = 0;
static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT;
@ -100,24 +99,23 @@ mod imp {
unsafe { mem::transmute(&global_args_ptr) }
}
// Copied from `os`.
unsafe fn load_argc_and_argv(argc: int, argv: **u8) -> Vec<Vec<u8>> {
use c_str::CString;
use ptr::RawPtr;
use libc;
use vec::Vec;
Vec::from_fn(argc as uint, |i| {
let cs = CString::new(*(argv as **libc::c_char).offset(i as int), false);
Vec::from_slice(cs.as_bytes_no_nul())
let base = *argv.offset(i as int);
let mut len = 0;
while *base.offset(len) != 0 { len += 1; }
slice::raw::buf_as_slice(base, len as uint, |slice| {
Vec::from_slice(slice)
})
})
}
#[cfg(test)]
mod tests {
use prelude::*;
use std::prelude::*;
use std::finally::Finally;
use super::*;
use finally::Finally;
#[test]
fn smoke_test() {
@ -149,8 +147,8 @@ mod imp {
#[cfg(target_os = "macos")]
#[cfg(target_os = "win32")]
mod imp {
use option::Option;
use vec::Vec;
use core::prelude::*;
use collections::vec::Vec;
pub unsafe fn init(_argc: int, _argv: **u8) {
}

View File

@ -0,0 +1,63 @@
// 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.
//! Implementation of running at_exit routines
//!
//! Documentation can be found on the `rt::at_exit` function.
use core::prelude::*;
use alloc::owned::Box;
use collections::vec::Vec;
use core::atomics;
use core::mem;
use exclusive::Exclusive;
type Queue = Exclusive<Vec<proc():Send>>;
static mut QUEUE: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
static mut RUNNING: atomics::AtomicBool = atomics::INIT_ATOMIC_BOOL;
pub fn init() {
let state: Box<Queue> = box Exclusive::new(Vec::new());
unsafe {
rtassert!(!RUNNING.load(atomics::SeqCst));
rtassert!(QUEUE.swap(mem::transmute(state), atomics::SeqCst) == 0);
}
}
pub fn push(f: proc():Send) {
unsafe {
// Note that the check against 0 for the queue pointer is not atomic at
// all with respect to `run`, meaning that this could theoretically be a
// use-after-free. There's not much we can do to protect against that,
// however. Let's just assume a well-behaved runtime and go from there!
rtassert!(!RUNNING.load(atomics::SeqCst));
let queue = QUEUE.load(atomics::SeqCst);
rtassert!(queue != 0);
(*(queue as *Queue)).lock().push(f);
}
}
pub fn run() {
let cur = unsafe {
rtassert!(!RUNNING.load(atomics::SeqCst));
let queue = QUEUE.swap(0, atomics::SeqCst);
rtassert!(queue != 0);
let queue: Box<Queue> = mem::transmute(queue);
mem::replace(&mut *queue.lock(), Vec::new())
};
for to_run in cur.move_iter() {
to_run();
}
}

View File

@ -18,11 +18,9 @@
//! each respective runtime to make sure that they call increment() and
//! decrement() manually.
#![experimental] // this is a massive code smell
#![doc(hidden)]
use core::atomics;
use sync::atomics;
use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
static mut TASK_COUNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
static mut TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;

View File

@ -65,24 +65,17 @@ fn main() {
*/
use clone::Clone;
use cmp::PartialEq;
use container::Container;
use iter::{Iterator, range};
use kinds::marker;
use core::prelude::*;
use alloc::libc_heap::malloc_raw;
use collections::string::String;
use core::kinds::marker;
use core::mem;
use core::ptr;
use core::raw::Slice;
use core::slice;
use core::str;
use libc;
use mem;
use ops::Drop;
use option::{Option, Some, None};
use ptr::RawPtr;
use ptr;
use raw::Slice;
use rt::libc_heap::malloc_raw;
use slice::{ImmutableVector, MutableVector};
use slice;
use str::StrSlice;
use str;
use string::String;
/// The representation of a C String.
///
@ -454,11 +447,12 @@ pub unsafe fn from_c_multistring(buf: *libc::c_char,
#[cfg(test)]
mod tests {
use prelude::*;
use super::*;
use std::prelude::*;
use std::ptr;
use std::task;
use libc;
use ptr;
use str::StrSlice;
use super::*;
#[test]
fn test_str_multistring_parsing() {
@ -574,7 +568,6 @@ mod tests {
#[test]
fn test_to_c_str_fail() {
use task;
assert!(task::try(proc() { "he\x00llo".to_c_str() }).is_err());
}
@ -700,10 +693,9 @@ mod tests {
#[cfg(test)]
mod bench {
extern crate test;
use self::test::Bencher;
use test::Bencher;
use libc;
use prelude::*;
use std::prelude::*;
#[inline]
fn check(s: &str, c_str: *libc::c_char) {

115
src/librustrt/exclusive.rs Normal file
View File

@ -0,0 +1,115 @@
// 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.
use core::prelude::*;
use core::ty::Unsafe;
use mutex;
/// An OS mutex over some data.
///
/// This is not a safe primitive to use, it is unaware of the libgreen
/// scheduler, as well as being easily susceptible to misuse due to the usage of
/// the inner NativeMutex.
///
/// > **Note**: This type is not recommended for general use. The mutex provided
/// > as part of `libsync` should almost always be favored.
pub struct Exclusive<T> {
lock: mutex::NativeMutex,
data: Unsafe<T>,
}
/// An RAII guard returned via `lock`
pub struct ExclusiveGuard<'a, T> {
// FIXME #12808: strange name to try to avoid interfering with
// field accesses of the contained type via Deref
_data: &'a mut T,
_guard: mutex::LockGuard<'a>,
}
impl<T: Send> Exclusive<T> {
/// Creates a new `Exclusive` which will protect the data provided.
pub fn new(user_data: T) -> Exclusive<T> {
Exclusive {
lock: unsafe { mutex::NativeMutex::new() },
data: Unsafe::new(user_data),
}
}
/// Acquires this lock, returning a guard which the data is accessed through
/// and from which that lock will be unlocked.
///
/// This method is unsafe due to many of the same reasons that the
/// NativeMutex itself is unsafe.
pub unsafe fn lock<'a>(&'a self) -> ExclusiveGuard<'a, T> {
let guard = self.lock.lock();
let data = &mut *self.data.get();
ExclusiveGuard {
_data: data,
_guard: guard,
}
}
}
impl<'a, T: Send> ExclusiveGuard<'a, T> {
// The unsafety here should be ok because our loan guarantees that the lock
// itself is not moving
pub fn signal(&self) {
unsafe { self._guard.signal() }
}
pub fn wait(&self) {
unsafe { self._guard.wait() }
}
}
impl<'a, T: Send> Deref<T> for ExclusiveGuard<'a, T> {
fn deref<'a>(&'a self) -> &'a T { &*self._data }
}
impl<'a, T: Send> DerefMut<T> for ExclusiveGuard<'a, T> {
fn deref_mut<'a>(&'a mut self) -> &'a mut T { &mut *self._data }
}
#[cfg(test)]
mod tests {
use std::prelude::*;
use alloc::arc::Arc;
use super::Exclusive;
use std::task;
#[test]
fn exclusive_new_arc() {
unsafe {
let mut futures = Vec::new();
let num_tasks = 10;
let count = 10;
let total = Arc::new(Exclusive::new(box 0));
for _ in range(0u, num_tasks) {
let total = total.clone();
let (tx, rx) = channel();
futures.push(rx);
task::spawn(proc() {
for _ in range(0u, count) {
**total.lock() += 1;
}
tx.send(());
});
};
for f in futures.mut_iter() { f.recv() }
assert_eq!(**total.lock(), num_tasks * count);
}
}
}

163
src/librustrt/lib.rs Normal file
View File

@ -0,0 +1,163 @@
// 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.
#![crate_id = "rustrt#0.11.0-pre"]
#![license = "MIT/ASL2"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
html_root_url = "http://doc.rust-lang.org/")]
#![feature(macro_rules, phase, globs, thread_local, managed_boxes, asm)]
#![no_std]
#![experimental]
#[phase(syntax, link)]
extern crate core;
extern crate alloc;
extern crate libc;
extern crate collections;
#[cfg(test)] extern crate realrustrt = "rustrt";
#[cfg(test)] extern crate test;
#[cfg(test)] extern crate native;
#[cfg(test)] #[phase(syntax, link)] extern crate std;
pub use self::util::{Stdio, Stdout, Stderr};
pub use self::unwind::{begin_unwind, begin_unwind_fmt};
use core::prelude::*;
use alloc::owned::Box;
use core::any::Any;
use task::{Task, BlockedTask, TaskOpts};
mod macros;
mod at_exit_imp;
mod local_ptr;
mod thread_local_storage;
mod util;
mod libunwind;
pub mod args;
pub mod bookkeeping;
pub mod exclusive;
pub mod local;
pub mod local_data;
pub mod local_heap;
pub mod mutex;
pub mod rtio;
pub mod stack;
pub mod task;
pub mod unwind;
pub mod c_str;
/// The interface to the current runtime.
///
/// This trait is used as the abstraction between 1:1 and M:N scheduling. The
/// two independent crates, libnative and libgreen, both have objects which
/// implement this trait. The goal of this trait is to encompass all the
/// fundamental differences in functionality between the 1:1 and M:N runtime
/// modes.
pub trait Runtime {
// Necessary scheduling functions, used for channels and blocking I/O
// (sometimes).
fn yield_now(~self, cur_task: Box<Task>);
fn maybe_yield(~self, cur_task: Box<Task>);
fn deschedule(~self, times: uint, cur_task: Box<Task>,
f: |BlockedTask| -> Result<(), BlockedTask>);
fn reawaken(~self, to_wake: Box<Task>);
// Miscellaneous calls which are very different depending on what context
// you're in.
fn spawn_sibling(~self,
cur_task: Box<Task>,
opts: TaskOpts,
f: proc():Send);
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
/// The (low, high) edges of the current stack.
fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
fn can_block(&self) -> bool;
// FIXME: This is a serious code smell and this should not exist at all.
fn wrap(~self) -> Box<Any>;
}
/// The default error code of the rust runtime if the main task fails instead
/// of exiting cleanly.
pub static DEFAULT_ERROR_CODE: int = 101;
/// One-time runtime initialization.
///
/// Initializes global state, including frobbing
/// the crate's logging flags, registering GC
/// metadata, and storing the process arguments.
pub fn init(argc: int, argv: **u8) {
// FIXME: Derefing these pointers is not safe.
// Need to propagate the unsafety to `start`.
unsafe {
args::init(argc, argv);
local_ptr::init();
at_exit_imp::init();
}
// FIXME(#14344) this shouldn't be necessary
collections::fixme_14344_be_sure_to_link_to_collections();
alloc::fixme_14344_be_sure_to_link_to_collections();
}
/// 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 tasks have exited.
///
/// The procedure is *not* executed with a local `Task` 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
/// are running, and doing so will lead to a process abort.
pub fn at_exit(f: proc():Send) {
at_exit_imp::push(f);
}
/// One-time runtime cleanup.
///
/// This function is unsafe because it performs no checks to ensure that the
/// runtime has completely ceased running. It is the responsibility of the
/// caller to ensure that the runtime is entirely shut down and nothing will be
/// poking around at the internal components.
///
/// Invoking cleanup while portions of the runtime are still in use may cause
/// undefined behavior.
pub unsafe fn cleanup() {
bookkeeping::wait_for_other_tasks();
at_exit_imp::run();
args::cleanup();
local_ptr::cleanup();
}
// FIXME: these probably shouldn't be public...
#[doc(hidden)]
pub mod shouldnt_be_public {
#[cfg(not(test))]
pub use super::local_ptr::native::maybe_tls_key;
#[cfg(not(windows), not(target_os = "android"))]
pub use super::local_ptr::compiled::RT_TLS_PTR;
}
#[cfg(not(test))]
mod std {
pub use core::{fmt, option, cmp};
}

View File

@ -79,10 +79,6 @@ pub type _Unwind_Exception_Cleanup_Fn =
extern "C" fn(unwind_code: _Unwind_Reason_Code,
exception: *_Unwind_Exception);
pub type _Unwind_Trace_Fn =
extern "C" fn(ctx: *_Unwind_Context,
arg: *libc::c_void) -> _Unwind_Reason_Code;
#[cfg(target_os = "linux")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "win32")]
@ -97,67 +93,4 @@ extern "C" {
pub fn _Unwind_RaiseException(exception: *_Unwind_Exception)
-> _Unwind_Reason_Code;
pub fn _Unwind_DeleteException(exception: *_Unwind_Exception);
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
trace_argument: *libc::c_void)
-> _Unwind_Reason_Code;
#[cfg(not(target_os = "android"),
not(target_os = "linux", target_arch = "arm"))]
pub fn _Unwind_GetIP(ctx: *_Unwind_Context) -> libc::uintptr_t;
#[cfg(not(target_os = "android"),
not(target_os = "linux", target_arch = "arm"))]
pub fn _Unwind_FindEnclosingFunction(pc: *libc::c_void) -> *libc::c_void;
}
// On android, the function _Unwind_GetIP is a macro, and this is the expansion
// of the macro. This is all copy/pasted directly from the header file with the
// definition of _Unwind_GetIP.
#[cfg(target_os = "android")]
#[cfg(target_os = "linux", target_arch = "arm")]
pub unsafe fn _Unwind_GetIP(ctx: *_Unwind_Context) -> libc::uintptr_t {
#[repr(C)]
enum _Unwind_VRS_Result {
_UVRSR_OK = 0,
_UVRSR_NOT_IMPLEMENTED = 1,
_UVRSR_FAILED = 2,
}
#[repr(C)]
enum _Unwind_VRS_RegClass {
_UVRSC_CORE = 0,
_UVRSC_VFP = 1,
_UVRSC_FPA = 2,
_UVRSC_WMMXD = 3,
_UVRSC_WMMXC = 4,
}
#[repr(C)]
enum _Unwind_VRS_DataRepresentation {
_UVRSD_UINT32 = 0,
_UVRSD_VFPX = 1,
_UVRSD_FPAX = 2,
_UVRSD_UINT64 = 3,
_UVRSD_FLOAT = 4,
_UVRSD_DOUBLE = 5,
}
type _Unwind_Word = libc::c_uint;
extern {
fn _Unwind_VRS_Get(ctx: *_Unwind_Context,
klass: _Unwind_VRS_RegClass,
word: _Unwind_Word,
repr: _Unwind_VRS_DataRepresentation,
data: *mut libc::c_void) -> _Unwind_VRS_Result;
}
let mut val: _Unwind_Word = 0;
let ptr = &mut val as *mut _Unwind_Word;
let _ = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 15, _UVRSD_UINT32,
ptr as *mut libc::c_void);
(val & !1) as libc::uintptr_t
}
// This function also doesn't exist on android or arm/linux, so make it a no-op
#[cfg(target_os = "android")]
#[cfg(target_os = "linux", target_arch = "arm")]
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *libc::c_void) -> *libc::c_void {
pc
}

View File

@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use option::Option;
use owned::Box;
use rt::task::Task;
use rt::local_ptr;
use core::prelude::*;
use alloc::owned::Box;
use local_ptr;
use task::Task;
/// Encapsulates some task-local data.
pub trait Local<Borrowed> {
@ -52,11 +53,10 @@ impl Local<local_ptr::Borrowed<Task>> for Task {
#[cfg(test)]
mod test {
use option::{None, Option};
use rt::thread::Thread;
use std::prelude::*;
use std::rt::thread::Thread;
use super::*;
use owned::Box;
use rt::task::Task;
use task::Task;
#[test]
fn thread_local_task_smoke_test() {

View File

@ -38,18 +38,16 @@ assert_eq!(*key_vector.get().unwrap(), ~[4]);
// Casting 'Arcane Sight' reveals an overwhelming aura of Transmutation
// magic.
use iter::{Iterator};
use kinds::Send;
use kinds::marker;
use mem::replace;
use mem;
use ops::{Drop, Deref};
use option::{None, Option, Some};
use owned::Box;
use raw;
use rt::task::{Task, LocalStorage};
use slice::{ImmutableVector, MutableVector};
use vec::Vec;
use core::prelude::*;
use alloc::owned::Box;
use collections::vec::Vec;
use core::kinds::marker;
use core::mem;
use core::raw;
use local::Local;
use task::{Task, LocalStorage};
/**
* Indexes a task-local data slot. This pointer is used for comparison to
@ -111,10 +109,10 @@ unsafe fn get_local_map() -> Option<&mut Map> {
// If this is the first time we've accessed TLS, perform similar
// actions to the oldsched way of doing things.
&LocalStorage(ref mut slot) => {
*slot = Some(vec!());
*slot = Some(Vec::new());
match *slot {
Some(ref mut map_ptr) => { return Some(map_ptr) }
None => unreachable!(),
None => fail!("unreachable code"),
}
}
}
@ -192,7 +190,7 @@ impl<T: 'static> KeyValue<T> {
match pos {
Some(i) => {
replace(map.get_mut(i), newval).map(|(_, data, _)| {
mem::replace(map.get_mut(i), newval).map(|(_, data, _)| {
// Move `data` into transmute to get out the memory that it
// owns, we must free it manually later.
let t: raw::TraitObject = unsafe { mem::transmute(data) };
@ -277,10 +275,9 @@ impl<T: 'static> Drop for Ref<T> {
#[cfg(test)]
mod tests {
use prelude::*;
use std::prelude::*;
use super::*;
use owned::Box;
use task;
use std::task;
#[test]
fn test_tls_multitask() {

View File

@ -10,55 +10,45 @@
//! The local, garbage collected heap
use alloc::util;
use iter::Iterator;
use libc::{c_void, free};
use mem;
use ops::Drop;
use option::{Option, None, Some};
use ptr::RawPtr;
use ptr;
use raw;
use rt::libc_heap;
use rt::local::Local;
use rt::task::Task;
use slice::{ImmutableVector, Vector};
use vec::Vec;
use core::prelude::*;
// This has no meaning with out rtdebug also turned on.
#[cfg(rtdebug)]
static TRACK_ALLOCATIONS: int = 0;
#[cfg(rtdebug)]
static MAGIC: u32 = 0xbadc0ffe;
use alloc::libc_heap;
use alloc::util;
use libc::{c_void, free};
use core::mem;
use core::ptr;
use core::raw;
use local::Local;
use task::Task;
static RC_IMMORTAL : uint = 0x77777777;
pub type Box = raw::Box<()>;
pub struct MemoryRegion {
allocations: Vec<*AllocHeader>,
live_allocations: uint,
}
pub struct LocalHeap {
memory_region: MemoryRegion,
live_allocs: *mut raw::Box<()>,
}
impl LocalHeap {
#[inline]
pub fn new() -> LocalHeap {
let region = MemoryRegion {
allocations: Vec::new(),
live_allocations: 0,
};
LocalHeap {
memory_region: region,
memory_region: MemoryRegion { live_allocations: 0 },
live_allocs: ptr::mut_null(),
}
}
#[inline]
pub fn alloc(&mut self, drop_glue: fn(*mut u8), size: uint, align: uint) -> *mut Box {
#[allow(deprecated)]
pub fn alloc(&mut self,
drop_glue: fn(*mut u8),
size: uint,
align: uint) -> *mut Box {
let total_size = util::get_box_size(size, align);
let alloc = self.memory_region.malloc(total_size);
{
@ -119,6 +109,63 @@ impl LocalHeap {
self.memory_region.free(alloc);
}
pub unsafe fn annihilate(&mut self) {
let mut n_total_boxes = 0u;
// Pass 1: Make all boxes immortal.
//
// In this pass, nothing gets freed, so it does not matter whether
// we read the next field before or after the callback.
self.each_live_alloc(true, |_, alloc| {
n_total_boxes += 1;
(*alloc).ref_count = RC_IMMORTAL;
});
// Pass 2: Drop all boxes.
//
// In this pass, unique-managed boxes may get freed, but not
// managed boxes, so we must read the `next` field *after* the
// callback, as the original value may have been freed.
self.each_live_alloc(false, |_, alloc| {
let drop_glue = (*alloc).drop_glue;
let data = &mut (*alloc).data as *mut ();
drop_glue(data as *mut u8);
});
// Pass 3: Free all boxes.
//
// In this pass, managed boxes may get freed (but not
// unique-managed boxes, though I think that none of those are
// left), so we must read the `next` field before, since it will
// not be valid after.
self.each_live_alloc(true, |me, alloc| {
me.free(alloc);
});
if debug_mem() {
// We do logging here w/o allocation.
rterrln!("total boxes annihilated: {}", n_total_boxes);
}
}
unsafe fn each_live_alloc(&mut self, read_next_before: bool,
f: |&mut LocalHeap, alloc: *mut raw::Box<()>|) {
//! Walks the internal list of allocations
let mut alloc = self.live_allocs;
while alloc != ptr::mut_null() {
let next_before = (*alloc).next;
f(self, alloc);
if read_next_before {
alloc = next_before;
} else {
alloc = (*alloc).next;
}
}
}
}
impl Drop for LocalHeap {
@ -127,43 +174,11 @@ impl Drop for LocalHeap {
}
}
#[cfg(rtdebug)]
struct AllocHeader {
magic: u32,
index: i32,
size: u32,
}
#[cfg(not(rtdebug))]
struct AllocHeader;
impl AllocHeader {
#[cfg(rtdebug)]
fn init(&mut self, size: u32) {
if TRACK_ALLOCATIONS > 0 {
self.magic = MAGIC;
self.index = -1;
self.size = size;
}
}
#[cfg(not(rtdebug))]
fn init(&mut self, _size: u32) {}
#[cfg(rtdebug)]
fn assert_sane(&self) {
if TRACK_ALLOCATIONS > 0 {
rtassert!(self.magic == MAGIC);
}
}
#[cfg(not(rtdebug))]
fn assert_sane(&self) {}
#[cfg(rtdebug)]
fn update_size(&mut self, size: u32) {
if TRACK_ALLOCATIONS > 0 {
self.size = size;
}
}
#[cfg(not(rtdebug))]
fn update_size(&mut self, _size: u32) {}
fn as_box(&mut self) -> *mut Box {
@ -183,6 +198,17 @@ impl AllocHeader {
}
}
#[cfg(unix)]
fn debug_mem() -> bool {
// FIXME: Need to port the environment struct to newsched
false
}
#[cfg(windows)]
fn debug_mem() -> bool {
false
}
impl MemoryRegion {
#[inline]
fn malloc(&mut self, size: uint) -> *mut Box {
@ -230,39 +256,10 @@ impl MemoryRegion {
}
}
#[cfg(rtdebug)]
fn claim(&mut self, alloc: &mut AllocHeader) {
alloc.assert_sane();
if TRACK_ALLOCATIONS > 1 {
alloc.index = self.allocations.len() as i32;
self.allocations.push(&*alloc as *AllocHeader);
}
}
#[cfg(not(rtdebug))]
#[inline]
fn claim(&mut self, _alloc: &mut AllocHeader) {}
#[cfg(rtdebug)]
fn release(&mut self, alloc: &AllocHeader) {
alloc.assert_sane();
if TRACK_ALLOCATIONS > 1 {
rtassert!(self.allocations.as_slice()[alloc.index] == alloc as *AllocHeader);
self.allocations.as_mut_slice()[alloc.index] = ptr::null();
}
}
#[cfg(not(rtdebug))]
#[inline]
fn release(&mut self, _alloc: &AllocHeader) {}
#[cfg(rtdebug)]
fn update(&mut self, alloc: &mut AllocHeader, orig: *AllocHeader) {
alloc.assert_sane();
if TRACK_ALLOCATIONS > 1 {
rtassert!(self.allocations.as_slice()[alloc.index] == orig);
self.allocations.as_mut_slice()[alloc.index] = &*alloc as *AllocHeader;
}
}
#[cfg(not(rtdebug))]
#[inline]
fn update(&mut self, _alloc: &mut AllocHeader, _orig: *AllocHeader) {}
}
@ -272,11 +269,9 @@ impl Drop for MemoryRegion {
if self.live_allocations != 0 {
rtabort!("leaked managed memory ({} objects)", self.live_allocations);
}
rtassert!(self.allocations.as_slice().iter().all(|s| s.is_null()));
}
}
#[cfg(not(test))]
#[lang="malloc"]
#[inline]
@ -318,10 +313,6 @@ pub unsafe fn local_free(ptr: *u8) {
}
}
pub fn live_allocs() -> *mut Box {
Local::borrow(None::<Task>).heap.live_allocs
}
#[cfg(test)]
mod bench {
extern crate test;

View File

@ -17,10 +17,10 @@
#![allow(dead_code)]
use mem;
use ops::{Drop, Deref, DerefMut};
use owned::Box;
use ptr::RawPtr;
use core::prelude::*;
use core::mem;
use alloc::owned::Box;
#[cfg(windows)] // mingw-w32 doesn't like thread_local things
#[cfg(target_os = "android")] // see #10686
@ -83,13 +83,13 @@ pub unsafe fn borrow<T>() -> Borrowed<T> {
/// it wherever possible.
#[cfg(not(windows), not(target_os = "android"))]
pub mod compiled {
use mem;
use option::{Option, Some, None};
use owned::Box;
use ptr::RawPtr;
use core::prelude::*;
use alloc::owned::Box;
use core::mem;
#[cfg(test)]
pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR;
pub use realrustrt::shouldnt_be_public::RT_TLS_PTR;
#[cfg(not(test))]
#[thread_local]
@ -234,12 +234,12 @@ pub mod compiled {
/// implementation uses the `thread_local_storage` module to provide a
/// thread-local value.
pub mod native {
use mem;
use option::{Option, Some, None};
use owned::Box;
use ptr::RawPtr;
use ptr;
use tls = rt::thread_local_storage;
use core::prelude::*;
use alloc::owned::Box;
use core::mem;
use core::ptr;
use tls = thread_local_storage;
static mut RT_TLS_KEY: tls::Key = -1;
@ -396,9 +396,9 @@ pub mod native {
#[inline] #[cfg(test)]
pub fn maybe_tls_key() -> Option<tls::Key> {
use realstd;
use realrustrt;
unsafe {
mem::transmute(realstd::rt::shouldnt_be_public::maybe_tls_key())
mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key())
}
}
}

View File

@ -16,8 +16,8 @@
#![macro_escape]
macro_rules! rterrln (
($($arg:tt)*) => ( {
format_args!(::rt::util::dumb_println, $($arg)*)
($fmt:expr $($arg:tt)*) => ( {
format_args!(::util::dumb_print, concat!($fmt, "\n") $($arg)*)
} )
)
@ -32,7 +32,7 @@ macro_rules! rtdebug (
macro_rules! rtassert (
( $arg:expr ) => ( {
if ::rt::util::ENFORCE_SANITY {
if ::util::ENFORCE_SANITY {
if !$arg {
rtabort!(" assertion failed: {}", stringify!($arg));
}
@ -42,8 +42,5 @@ macro_rules! rtassert (
macro_rules! rtabort (
($($arg:tt)*) => ( {
use str::Str;
::rt::util::abort(format!($($arg)*).as_slice());
} )
($($arg:tt)*) => (format_args!(::util::abort, $($arg)*))
)

View File

@ -33,7 +33,7 @@
//! # Example
//!
//! ```rust
//! use std::unstable::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT};
//! use std::rt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT};
//!
//! // Use a statically initialized mutex
//! static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
@ -58,8 +58,7 @@
#![allow(non_camel_case_types)]
use option::{Option, None, Some};
use ops::Drop;
use core::prelude::*;
/// A native mutex suitable for storing in statics (that is, it has
/// the `destroy` method rather than a destructor).
@ -109,7 +108,7 @@ impl StaticNativeMutex {
/// # Example
///
/// ```rust
/// use std::unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
/// use std::rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
/// static mut LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
/// unsafe {
/// let _guard = LOCK.lock();
@ -183,7 +182,7 @@ impl NativeMutex {
///
/// # Example
/// ```rust
/// use std::unstable::mutex::NativeMutex;
/// use std::rt::mutex::NativeMutex;
/// unsafe {
/// let mut lock = NativeMutex::new();
///
@ -264,8 +263,8 @@ mod imp {
use libc;
use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,
pthread_mutex_t, pthread_cond_t};
use ty::Unsafe;
use kinds::marker;
use core::ty::Unsafe;
use core::kinds::marker;
type pthread_mutexattr_t = libc::c_void;
type pthread_condattr_t = libc::c_void;
@ -432,11 +431,11 @@ mod imp {
#[cfg(windows)]
mod imp {
use rt::libc_heap::malloc_raw;
use alloc::libc_heap::malloc_raw;
use core::atomics;
use core::ptr;
use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
use libc;
use ptr;
use sync::atomics;
type LPCRITICAL_SECTION = *mut c_void;
static SPIN_COUNT: DWORD = 4000;
@ -563,11 +562,11 @@ mod imp {
#[cfg(test)]
mod test {
use prelude::*;
use std::prelude::*;
use mem::drop;
use std::mem::drop;
use super::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use rt::thread::Thread;
use std::rt::thread::Thread;
#[test]
fn smoke_lock() {

View File

@ -10,32 +10,18 @@
//! The EventLoop and internal synchronous I/O interface.
use c_str::CString;
use comm::{Sender, Receiver};
use kinds::Send;
use core::prelude::*;
use alloc::owned::Box;
use collections::string::String;
use collections::vec::Vec;
use core::fmt;
use core::mem;
use libc::c_int;
use libc;
use mem;
use ops::Drop;
use option::{Option, Some, None};
use owned::Box;
use result::Err;
use rt::local::Local;
use rt::task::Task;
use vec::Vec;
use ai = io::net::addrinfo;
use io;
use io::IoResult;
use io::net::ip::{IpAddr, SocketAddr};
use io::process::{StdioContainer, ProcessExit};
use io::signal::Signum;
use io::{FileMode, FileAccess, FileStat, FilePermission};
use io::{SeekStyle};
pub trait Callback {
fn call(&mut self);
}
use c_str::CString;
use local::Local;
use task::Task;
pub trait EventLoop {
fn run(&mut self);
@ -50,6 +36,10 @@ pub trait EventLoop {
fn has_active_io(&self) -> bool;
}
pub trait Callback {
fn call(&mut self);
}
pub trait RemoteCallback {
/// Trigger the remote callback. Note that the number of times the
/// callback is run is not guaranteed. All that is guaranteed is
@ -172,9 +162,15 @@ impl<'a> LocalIo<'a> {
pub fn maybe_raise<T>(f: |io: &mut IoFactory| -> IoResult<T>)
-> IoResult<T>
{
#[cfg(unix)] use ERROR = libc::EINVAL;
#[cfg(windows)] use ERROR = libc::ERROR_CALL_NOT_IMPLEMENTED;
match LocalIo::borrow() {
None => Err(io::standard_error(io::IoUnavailable)),
Some(mut io) => f(io.get()),
None => Err(IoError {
code: ERROR as uint,
extra: 0,
detail: None,
}),
}
}
@ -185,11 +181,8 @@ impl<'a> LocalIo<'a> {
/// Returns the underlying I/O factory as a trait reference.
#[inline]
pub fn get<'a>(&'a mut self) -> &'a mut IoFactory {
// FIXME(pcwalton): I think this is actually sound? Could borrow check
// allow this safely?
unsafe {
mem::transmute_copy(&self.factory)
}
let f: &'a mut IoFactory = self.factory;
f
}
}
@ -206,7 +199,8 @@ pub trait IoFactory {
fn unix_connect(&mut self, path: &CString,
timeout: Option<u64>) -> IoResult<Box<RtioPipe:Send>>;
fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
hint: Option<ai::Hint>) -> IoResult<Vec<ai::Info>>;
hint: Option<AddrinfoHint>)
-> IoResult<Vec<AddrinfoInfo>>;
// filesystem operations
fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior)
@ -215,10 +209,8 @@ pub trait IoFactory {
-> IoResult<Box<RtioFileStream:Send>>;
fn fs_unlink(&mut self, path: &CString) -> IoResult<()>;
fn fs_stat(&mut self, path: &CString) -> IoResult<FileStat>;
fn fs_mkdir(&mut self, path: &CString,
mode: FilePermission) -> IoResult<()>;
fn fs_chmod(&mut self, path: &CString,
mode: FilePermission) -> IoResult<()>;
fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>;
fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>;
fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>;
fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>;
fn fs_readdir(&mut self, path: &CString, flags: c_int) ->
@ -241,7 +233,7 @@ pub trait IoFactory {
fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<RtioPipe:Send>>;
fn tty_open(&mut self, fd: c_int, readable: bool)
-> IoResult<Box<RtioTTY:Send>>;
fn signal(&mut self, signal: Signum, channel: Sender<Signum>)
fn signal(&mut self, signal: int, cb: Box<Callback:Send>)
-> IoResult<Box<RtioSignal:Send>>;
}
@ -300,8 +292,8 @@ pub trait RtioUdpSocket : RtioSocket {
pub trait RtioTimer {
fn sleep(&mut self, msecs: u64);
fn oneshot(&mut self, msecs: u64) -> Receiver<()>;
fn period(&mut self, msecs: u64) -> Receiver<()>;
fn oneshot(&mut self, msecs: u64, cb: Box<Callback:Send>);
fn period(&mut self, msecs: u64, cb: Box<Callback:Send>);
}
pub trait RtioFileStream {
@ -359,3 +351,99 @@ pub trait PausableIdleCallback {
}
pub trait RtioSignal {}
pub struct IoError {
pub code: uint,
pub extra: uint,
pub detail: Option<String>,
}
pub type IoResult<T> = Result<T, IoError>;
#[deriving(PartialEq, Eq)]
pub enum IpAddr {
Ipv4Addr(u8, u8, u8, u8),
Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16),
}
impl fmt::Show for IpAddr {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
Ipv4Addr(a, b, c, d) => write!(fmt, "{}.{}.{}.{}", a, b, c, d),
Ipv6Addr(a, b, c, d, e, f, g, h) => {
write!(fmt,
"{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}",
a, b, c, d, e, f, g, h)
}
}
}
}
#[deriving(PartialEq, Eq)]
pub struct SocketAddr {
pub ip: IpAddr,
pub port: u16,
}
pub enum StdioContainer {
Ignored,
InheritFd(i32),
CreatePipe(bool, bool),
}
pub enum ProcessExit {
ExitStatus(int),
ExitSignal(int),
}
pub enum FileMode {
Open,
Append,
Truncate,
}
pub enum FileAccess {
Read,
Write,
ReadWrite,
}
pub struct FileStat {
pub size: u64,
pub kind: u64,
pub perm: u64,
pub created: u64,
pub modified: u64,
pub accessed: u64,
pub device: u64,
pub inode: u64,
pub rdev: u64,
pub nlink: u64,
pub uid: u64,
pub gid: u64,
pub blksize: u64,
pub blocks: u64,
pub flags: u64,
pub gen: u64,
}
pub enum SeekStyle {
SeekSet,
SeekEnd,
SeekCur,
}
pub struct AddrinfoHint {
pub family: uint,
pub socktype: uint,
pub protocol: uint,
pub flags: uint,
}
pub struct AddrinfoInfo {
pub address: SocketAddr,
pub family: uint,
pub socktype: uint,
pub protocol: uint,
pub flags: uint,
}

View File

@ -33,12 +33,11 @@ pub static RED_ZONE: uint = 20 * 1024;
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
use option::{Option, None, Some};
use owned::Box;
use rt::local::Local;
use rt::task::Task;
use str::Str;
use intrinsics;
use core::prelude::*;
use alloc::owned::Box;
use local::Local;
use task::Task;
use core::intrinsics;
unsafe {
// We're calling this function because the stack just ran out. We need

View File

@ -13,30 +13,23 @@
//! local storage, and logging. Even a 'freestanding' Rust would likely want
//! to implement this.
use alloc::arc::Arc;
use core::prelude::*;
use alloc::arc::Arc;
use alloc::owned::{AnyOwnExt, Box};
use core::any::Any;
use core::iter::Take;
use core::mem;
use core::finally::Finally;
use core::atomics::{AtomicUint, SeqCst};
use cleanup;
use clone::Clone;
use comm::Sender;
use io::Writer;
use iter::{Iterator, Take};
use kinds::Send;
use local_data;
use mem;
use ops::Drop;
use option::{Option, Some, None};
use owned::{AnyOwnExt, Box};
use prelude::drop;
use result::{Result, Ok, Err};
use rt::Runtime;
use rt::local::Local;
use rt::local_heap::LocalHeap;
use rt::rtio::LocalIo;
use rt::unwind::Unwinder;
use str::SendStr;
use sync::atomics::{AtomicUint, SeqCst};
use task::{TaskResult, TaskOpts};
use finally::Finally;
use Runtime;
use local::Local;
use local_heap::LocalHeap;
use rtio::LocalIo;
use unwind::Unwinder;
use collections::str::SendStr;
/// The Task struct represents all state associated with a rust
/// task. There are at this point two primary "subtypes" of task,
@ -52,12 +45,26 @@ pub struct Task {
pub destroyed: bool,
pub name: Option<SendStr>,
pub stdout: Option<Box<Writer:Send>>,
pub stderr: Option<Box<Writer:Send>>,
imp: Option<Box<Runtime:Send>>,
}
pub struct TaskOpts {
/// Invoke this procedure with the result of the task when it finishes.
pub on_exit: Option<proc(Result):Send>,
/// A name for the task-to-be, for identification in failure messages
pub name: Option<SendStr>,
/// The size of the stack for the spawned task
pub stack_size: Option<uint>,
}
/// Indicates the manner in which a task exited.
///
/// A task that completes without failing is considered to exit successfully.
///
/// If you wish for this result's delivery to block until all
/// children tasks complete, recommend using a result future.
pub type Result = ::core::result::Result<(), Box<Any:Send>>;
pub struct GarbageCollector;
pub struct LocalStorage(pub Option<local_data::Map>);
@ -69,17 +76,9 @@ pub enum BlockedTask {
Shared(Arc<AtomicUint>),
}
pub enum DeathAction {
/// Action to be done with the exit code. If set, also makes the task wait
/// until all its watched children exit before collecting the status.
Execute(proc(TaskResult):Send),
/// A channel to send the result of the task on when the task exits
SendMessage(Sender<TaskResult>),
}
/// Per-task state related to task death, killing, failure, etc.
pub struct Death {
pub on_exit: Option<DeathAction>,
pub on_exit: Option<proc(Result):Send>,
}
pub struct BlockedTasks {
@ -96,8 +95,6 @@ impl Task {
death: Death::new(),
destroyed: false,
name: None,
stdout: None,
stderr: None,
imp: None,
}
}
@ -126,20 +123,6 @@ impl Task {
// Run the task main function, then do some cleanup.
f.finally(|| {
#[allow(unused_must_use)]
fn close_outputs() {
let mut task = Local::borrow(None::<Task>);
let stderr = task.stderr.take();
let stdout = task.stdout.take();
drop(task);
match stdout { Some(mut w) => { w.flush(); }, None => {} }
match stderr { Some(mut w) => { w.flush(); }, None => {} }
}
// First, flush/destroy the user stdout/logger because these
// destructors can run arbitrary code.
close_outputs();
// First, destroy task-local storage. This may run user dtors.
//
// FIXME #8302: Dear diary. I'm so tired and confused.
@ -168,14 +151,11 @@ impl Task {
drop(storage_map);
// Destroy remaining boxes. Also may run user dtors.
unsafe { cleanup::annihilate(); }
// Finally, just in case user dtors printed/logged during TLS
// cleanup and annihilation, re-destroy stdout and the logger.
// Note that these will have been initialized with a
// runtime-provided type which we have control over what the
// destructor does.
close_outputs();
let mut task = Local::borrow(None::<Task>);
let mut heap = mem::replace(&mut task.heap, LocalHeap::new());
drop(task);
unsafe { heap.annihilate() }
drop(heap);
})
};
@ -247,7 +227,7 @@ impl Task {
/// recommended to use this function directly, but rather communication
/// primitives in `std::comm` should be used.
pub fn deschedule(mut ~self, amt: uint,
f: |BlockedTask| -> Result<(), BlockedTask>) {
f: |BlockedTask| -> ::core::result::Result<(), BlockedTask>) {
let ops = self.imp.take_unwrap();
ops.deschedule(amt, self, f)
}
@ -303,6 +283,12 @@ impl Drop for Task {
}
}
impl TaskOpts {
pub fn new() -> TaskOpts {
TaskOpts { on_exit: None, name: None, stack_size: None }
}
}
impl Iterator<BlockedTask> for BlockedTasks {
fn next(&mut self) -> Option<BlockedTask> {
Some(Shared(self.inner.clone()))
@ -389,10 +375,9 @@ impl Death {
}
/// Collect failure exit codes from children and propagate them to a parent.
pub fn collect_failure(&mut self, result: TaskResult) {
pub fn collect_failure(&mut self, result: Result) {
match self.on_exit.take() {
Some(Execute(f)) => f(result),
Some(SendMessage(ch)) => { let _ = ch.send_opt(result); }
Some(f) => f(result),
None => {}
}
}
@ -407,8 +392,8 @@ impl Drop for Death {
#[cfg(test)]
mod test {
use super::*;
use prelude::*;
use task;
use std::prelude::*;
use std::task;
#[test]
fn local_heap() {
@ -440,16 +425,11 @@ mod test {
#[test]
fn rng() {
use rand::{StdRng, Rng};
use std::rand::{StdRng, Rng};
let mut r = StdRng::new().ok().unwrap();
let _ = r.next_u32();
}
#[test]
fn logging() {
info!("here i am. logging in a newsched task");
}
#[test]
fn comm_stream() {
let (tx, rx) = channel();
@ -466,8 +446,7 @@ mod test {
#[test]
fn heap_cycles() {
use cell::RefCell;
use option::{Option, Some, None};
use std::cell::RefCell;
struct List {
next: Option<@RefCell<List>>,
@ -485,7 +464,7 @@ mod test {
#[test]
#[should_fail]
fn test_begin_unwind() {
use rt::unwind::begin_unwind;
use std::rt::unwind::begin_unwind;
begin_unwind("cause", file!(), line!())
}

View File

@ -10,26 +10,21 @@
#![allow(dead_code)]
#[cfg(test)]
use owned::Box;
#[cfg(unix)]
use libc::c_int;
#[cfg(unix)]
use ptr::null;
#[cfg(windows)]
use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
#[cfg(unix)] use libc::c_int;
#[cfg(unix)] use core::ptr::null;
#[cfg(windows)] use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL};
#[cfg(unix)]
pub type Key = pthread_key_t;
#[cfg(unix)]
pub unsafe fn create(key: &mut Key) {
assert_eq!(0, pthread_key_create(key, null()));
assert!(pthread_key_create(key, null()) == 0);
}
#[cfg(unix)]
pub unsafe fn set(key: Key, value: *mut u8) {
assert_eq!(0, pthread_setspecific(key, value));
assert!(pthread_setspecific(key, value) == 0);
}
#[cfg(unix)]
@ -39,7 +34,7 @@ pub unsafe fn get(key: Key) -> *mut u8 {
#[cfg(unix)]
pub unsafe fn destroy(key: Key) {
assert_eq!(0, pthread_key_delete(key));
assert!(pthread_key_delete(key) == 0);
}
#[cfg(target_os="macos")]
@ -94,19 +89,25 @@ extern "system" {
fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL;
}
#[test]
fn tls_smoke_test() {
use mem::transmute;
unsafe {
let mut key = 0;
let value = box 20;
create(&mut key);
set(key, transmute(value));
let value: Box<int> = transmute(get(key));
assert_eq!(value, box 20);
let value = box 30;
set(key, transmute(value));
let value: Box<int> = transmute(get(key));
assert_eq!(value, box 30);
#[cfg(test)]
mod test {
use std::prelude::*;
use super::*;
#[test]
fn tls_smoke_test() {
use std::mem::transmute;
unsafe {
let mut key = 0;
let value = box 20;
create(&mut key);
set(key, transmute(value));
let value: Box<int> = transmute(get(key));
assert_eq!(value, box 20);
let value = box 30;
set(key, transmute(value));
let value: Box<int> = transmute(get(key));
assert_eq!(value, box 30);
}
}
}

View File

@ -8,73 +8,74 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Stack unwinding
//! Implementation of Rust stack unwinding
//!
//! For background on exception handling and stack unwinding please see
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
//! documents linked from it.
//! These are also good reads:
//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
//! http://www.airs.com/blog/index.php?s=exception+frames
//!
//! ## A brief summary
//!
//! Exception handling happens in two phases: a search phase and a cleanup phase.
//!
//! In both phases the unwinder walks stack frames from top to bottom using
//! information from the stack frame unwind sections of the current process's
//! modules ("module" here refers to an OS module, i.e. an executable or a
//! dynamic library).
//!
//! For each stack frame, it invokes the associated "personality routine", whose
//! address is also stored in the unwind info section.
//!
//! In the search phase, the job of a personality routine is to examine exception
//! object being thrown, and to decide whether it should be caught at that stack
//! frame. Once the handler frame has been identified, cleanup phase begins.
//!
//! In the cleanup phase, personality routines invoke cleanup code associated
//! with their stack frames (i.e. destructors). Once stack has been unwound down
//! to the handler frame level, unwinding stops and the last personality routine
//! transfers control to its' catch block.
//!
//! ## Frame unwind info registration
//!
//! Each module has its' own frame unwind info section (usually ".eh_frame"), and
//! unwinder needs to know about all of them in order for unwinding to be able to
//! cross module boundaries.
//!
//! On some platforms, like Linux, this is achieved by dynamically enumerating
//! currently loaded modules via the dl_iterate_phdr() API and finding all
//! .eh_frame sections.
//!
//! Others, like Windows, require modules to actively register their unwind info
//! sections by calling __register_frame_info() API at startup. In the latter
//! case it is essential that there is only one copy of the unwinder runtime in
//! the process. This is usually achieved by linking to the dynamic version of
//! the unwind runtime.
//!
//! Currently Rust uses unwind runtime provided by libgcc.
// Implementation of Rust stack unwinding
//
// For background on exception handling and stack unwinding please see
// "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
// documents linked from it.
// These are also good reads:
// http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
// http://monoinfinito.wordpress.com/series/exception-handling-in-c/
// http://www.airs.com/blog/index.php?s=exception+frames
//
// ~~~ A brief summary ~~~
// Exception handling happens in two phases: a search phase and a cleanup phase.
//
// In both phases the unwinder walks stack frames from top to bottom using
// information from the stack frame unwind sections of the current process's
// modules ("module" here refers to an OS module, i.e. an executable or a
// dynamic library).
//
// For each stack frame, it invokes the associated "personality routine", whose
// address is also stored in the unwind info section.
//
// In the search phase, the job of a personality routine is to examine exception
// object being thrown, and to decide whether it should be caught at that stack
// frame. Once the handler frame has been identified, cleanup phase begins.
//
// In the cleanup phase, personality routines invoke cleanup code associated
// with their stack frames (i.e. destructors). Once stack has been unwound down
// to the handler frame level, unwinding stops and the last personality routine
// transfers control to its' catch block.
//
// ~~~ Frame unwind info registration ~~~
// Each module has its' own frame unwind info section (usually ".eh_frame"), and
// unwinder needs to know about all of them in order for unwinding to be able to
// cross module boundaries.
//
// On some platforms, like Linux, this is achieved by dynamically enumerating
// currently loaded modules via the dl_iterate_phdr() API and finding all
// .eh_frame sections.
//
// Others, like Windows, require modules to actively register their unwind info
// sections by calling __register_frame_info() API at startup. In the latter
// case it is essential that there is only one copy of the unwinder runtime in
// the process. This is usually achieved by linking to the dynamic version of
// the unwind runtime.
//
// Currently Rust uses unwind runtime provided by libgcc.
use core::prelude::*;
use any::{Any, AnyRefExt};
use fmt;
use intrinsics;
use kinds::Send;
use mem;
use option::{Some, None, Option};
use owned::Box;
use prelude::drop;
use ptr::RawPtr;
use result::{Err, Ok, Result};
use rt::backtrace;
use rt::local::Local;
use rt::task::Task;
use str::Str;
use string::String;
use task::TaskResult;
use alloc::owned::Box;
use collections::string::String;
use collections::vec::Vec;
use core::any::Any;
use core::atomics;
use core::cmp;
use core::fmt;
use core::intrinsics;
use core::mem;
use core::raw::Closure;
use libc::c_void;
use uw = rt::libunwind;
use local::Local;
use task::{Task, Result};
use exclusive::Exclusive;
use uw = libunwind;
pub struct Unwinder {
unwinding: bool,
@ -86,6 +87,24 @@ struct Exception {
cause: Option<Box<Any:Send>>,
}
pub type Callback = fn(msg: &Any:Send, file: &'static str, line: uint);
type Queue = Exclusive<Vec<Callback>>;
// Variables used for invoking callbacks when a task starts to unwind.
//
// For more information, see below.
static MAX_CALLBACKS: uint = 16;
static mut CALLBACKS: [atomics::AtomicUint, ..MAX_CALLBACKS] =
[atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT,
atomics::INIT_ATOMIC_UINT, atomics::INIT_ATOMIC_UINT];
static mut CALLBACK_CNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
impl Unwinder {
pub fn new() -> Unwinder {
Unwinder {
@ -158,6 +177,7 @@ pub unsafe fn try(f: ||) -> Result<(), Box<Any:Send>> {
}
}
#[link(name = "rustrt_native", kind = "static")]
extern {
// Rust's try-catch
// When f(...) returns normally, the return value is null.
@ -227,7 +247,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
#[doc(hidden)]
#[allow(visible_private_types)]
pub mod eabi {
use uw = rt::libunwind;
use uw = libunwind;
use libc::c_int;
extern "C" {
@ -338,11 +358,26 @@ pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
#[inline(never)] #[cold]
pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str,
line: uint) -> ! {
use core::fmt::FormatWriter;
// We do two allocations here, unfortunately. But (a) they're
// required with the current scheme, and (b) we don't handle
// failure + OOM properly anyway (see comment in begin_unwind
// below).
begin_unwind_inner(box fmt::format(msg), file, line)
struct VecWriter<'a> { v: &'a mut Vec<u8> }
impl<'a> fmt::FormatWriter for VecWriter<'a> {
fn write(&mut self, buf: &[u8]) -> fmt::Result {
self.v.push_all(buf);
Ok(())
}
}
let mut v = Vec::new();
let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg);
begin_unwind_inner(box String::from_utf8(v).unwrap(), file, line)
}
/// This is the entry point of unwinding for fail!() and assert!().
@ -373,122 +408,82 @@ pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> !
fn begin_unwind_inner(msg: Box<Any:Send>,
file: &'static str,
line: uint) -> ! {
// First up, print the message that we're failing
print_failure(msg, file, line);
let opt_task: Option<Box<Task>> = Local::try_take();
match opt_task {
Some(mut task) => {
// Now that we've printed why we're failing, do a check
// to make sure that we're not double failing.
//
// If a task fails while it's already unwinding then we
// have limited options. Currently our preference is to
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the task cleanly.
if task.unwinder.unwinding {
rterrln!("task failed during unwinding. aborting.");
// Don't print the backtrace twice (it would have already been
// printed if logging was enabled).
if !backtrace::log_enabled() {
let mut err = ::rt::util::Stderr;
let _err = backtrace::write(&mut err);
}
unsafe { intrinsics::abort() }
// First, invoke call the user-defined callbacks triggered on task failure.
//
// By the time that we see a callback has been registered (by reading
// MAX_CALLBACKS), the actuall callback itself may have not been stored yet,
// so we just chalk it up to a race condition and move on to the next
// callback. Additionally, CALLBACK_CNT may briefly be higher than
// MAX_CALLBACKS, so we're sure to clamp it as necessary.
let callbacks = unsafe {
let amt = CALLBACK_CNT.load(atomics::SeqCst);
CALLBACKS.slice_to(cmp::min(amt, MAX_CALLBACKS))
};
for cb in callbacks.iter() {
match cb.load(atomics::SeqCst) {
0 => {}
n => {
let f: Callback = unsafe { mem::transmute(n) };
f(msg, file, line);
}
// Finally, we've printed our failure and figured out we're not in a
// double failure, so flag that we've started to unwind and then
// actually unwind. Be sure that the task is in TLS so destructors
// can do fun things like I/O.
task.unwinder.unwinding = true;
Local::put(task);
}
None => {}
};
// Now that we've run all the necessary unwind callbacks, we actually
// perform the unwinding. If we don't have a task, then it's time to die
// (hopefully someone printed something about this).
let task: Box<Task> = match Local::try_take() {
Some(task) => task,
None => unsafe { intrinsics::abort() }
};
if task.unwinder.unwinding {
// If a task fails while it's already unwinding then we
// have limited options. Currently our preference is to
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the task cleanly.
rterrln!("task failed during unwinding. aborting.");
unsafe { intrinsics::abort() }
}
rust_fail(msg)
}
/// Given a failure message and the location that it occurred, prints the
/// message to the local task's appropriate stream.
///
/// This function currently handles three cases:
///
/// - There is no local task available. In this case the error is printed to
/// stderr.
/// - There is a local task available, but it does not have a stderr handle.
/// In this case the message is also printed to stderr.
/// - There is a local task available, and it has a stderr handle. The
/// message is printed to the handle given in this case.
fn print_failure(msg: &Any:Send, file: &str, line: uint) {
let msg = match msg.as_ref::<&'static str>() {
Some(s) => *s,
None => match msg.as_ref::<String>() {
Some(s) => s.as_slice(),
None => "Box<Any>",
}
};
// It is assumed that all reasonable rust code will have a local task at
// all times. This means that this `try_take` will succeed almost all of
// the time. There are border cases, however, when the runtime has
// *almost* set up the local task, but hasn't quite gotten there yet. In
// order to get some better diagnostics, we print on failure and
// immediately abort the whole process if there is no local task
// available.
let mut task: Box<Task> = match Local::try_take() {
Some(t) => t,
None => {
rterrln!("failed at '{}', {}:{}", msg, file, line);
if backtrace::log_enabled() {
let mut err = ::rt::util::Stderr;
let _err = backtrace::write(&mut err);
} else {
rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
}
return
}
};
// See comments in io::stdio::with_task_stdout as to why we have to be
// careful when using an arbitrary I/O handle from the task. We
// essentially need to dance to make sure when a task is in TLS when
// running user code.
let name = task.name.take();
{
let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
match task.stderr.take() {
Some(mut stderr) => {
Local::put(task);
// FIXME: what to do when the task printing fails?
let _err = write!(stderr,
"task '{}' failed at '{}', {}:{}\n",
n, msg, file, line);
if backtrace::log_enabled() {
let _err = backtrace::write(stderr);
}
task = Local::take();
match mem::replace(&mut task.stderr, Some(stderr)) {
Some(prev) => {
Local::put(task);
drop(prev);
task = Local::take();
}
None => {}
}
}
None => {
rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line);
if backtrace::log_enabled() {
let mut err = ::rt::util::Stderr;
let _err = backtrace::write(&mut err);
}
}
}
// Put the task back in TLS because the unwinding process may run code which
// requires the task. We need a handle to its unwinder, however, so after
// this we unsafely extract it and continue along.
Local::put(task);
unsafe {
let task: *mut Task = Local::unsafe_borrow();
(*task).unwinder.begin_unwind(msg);
}
task.name = name;
Local::put(task);
}
/// Register a callback to be invoked when a task unwinds.
///
/// This is an unsafe and experimental API which allows for an arbitrary
/// callback to be invoked when a task fails. This callback is invoked on both
/// the initial unwinding and a double unwinding if one occurs. Additionally,
/// the local `Task` will be in place for the duration of the callback, and
/// the callback must ensure that it remains in place once the callback returns.
///
/// Only a limited number of callbacks can be registered, and this function
/// returns whether the callback was successfully registered or not. It is not
/// currently possible to unregister a callback once it has been registered.
#[experimental]
pub unsafe fn register(f: Callback) -> bool {
match CALLBACK_CNT.fetch_add(1, atomics::SeqCst) {
// The invocation code has knowledge of this window where the count has
// been incremented, but the callback has not been stored. We're
// guaranteed that the slot we're storing into is 0.
n if n < MAX_CALLBACKS => {
let prev = CALLBACKS[n].swap(mem::transmute(f), atomics::SeqCst);
rtassert!(prev == 0);
true
}
// If we accidentally bumped the count too high, pull it back.
_ => {
CALLBACK_CNT.store(MAX_CALLBACKS, atomics::SeqCst);
false
}
}
}

131
src/librustrt/util.rs Normal file
View File

@ -0,0 +1,131 @@
// 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.
use core::prelude::*;
use core::cmp;
use core::fmt;
use core::intrinsics;
use core::slice;
use core::str;
use libc;
// 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 static ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) ||
cfg!(rtassert);
pub struct Stdio(libc::c_int);
pub static Stdout: Stdio = Stdio(libc::STDOUT_FILENO);
pub static Stderr: Stdio = Stdio(libc::STDERR_FILENO);
impl fmt::FormatWriter for Stdio {
fn write(&mut self, data: &[u8]) -> fmt::Result {
#[cfg(unix)]
type WriteLen = libc::size_t;
#[cfg(windows)]
type WriteLen = libc::c_uint;
unsafe {
let Stdio(fd) = *self;
libc::write(fd,
data.as_ptr() as *libc::c_void,
data.len() as WriteLen);
}
Ok(()) // yes, we're lying
}
}
pub fn dumb_print(args: &fmt::Arguments) {
use core::fmt::FormatWriter;
let mut w = Stderr;
let _ = w.write_fmt(args);
}
pub fn abort(args: &fmt::Arguments) -> ! {
use core::fmt::FormatWriter;
struct BufWriter<'a> {
buf: &'a mut [u8],
pos: uint,
}
impl<'a> FormatWriter for BufWriter<'a> {
fn write(&mut self, bytes: &[u8]) -> fmt::Result {
let left = self.buf.mut_slice_from(self.pos);
let to_write = bytes.slice_to(cmp::min(bytes.len(), left.len()));
slice::bytes::copy_memory(left, to_write);
self.pos += to_write.len();
Ok(())
}
}
// Convert the arguments into a stack-allocated string
let mut msg = [0u8, ..512];
let mut w = BufWriter { buf: msg, pos: 0 };
let _ = write!(&mut w, "{}", args);
let msg = str::from_utf8(w.buf.slice_to(w.pos)).unwrap_or("aborted");
let msg = if msg.is_empty() {
"aborted"
} else { "aborted" };
// Give some context to the message
let hash = msg.bytes().fold(0, |accum, val| accum + (val as uint) );
let quote = match hash % 10 {
0 => "
It was from the artists and poets that the pertinent answers came, and I
know that panic would have broken loose had they been able to compare notes.
As it was, lacking their original letters, I half suspected the compiler of
having asked leading questions, or of having edited the correspondence in
corroboration of what he had latently resolved to see.",
1 => "
There are not many persons who know what wonders are opened to them in the
stories and visions of their youth; for when as children we listen and dream,
we think but half-formed thoughts, and when as men we try to remember, we are
dulled and prosaic with the poison of life. But some of us awake in the night
with strange phantasms of enchanted hills and gardens, of fountains that sing
in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch
down to sleeping cities of bronze and stone, and of shadowy companies of heroes
that ride caparisoned white horses along the edges of thick forests; and then
we know that we have looked back through the ivory gates into that world of
wonder which was ours before we were wise and unhappy.",
2 => "
Instead of the poems I had hoped for, there came only a shuddering blackness
and ineffable loneliness; and I saw at last a fearful truth which no one had
ever dared to breathe before the unwhisperable secret of secrets The fact
that this city of stone and stridor is not a sentient perpetuation of Old New
York as London is of Old London and Paris of Old Paris, but that it is in fact
quite dead, its sprawling body imperfectly embalmed and infested with queer
animate things which have nothing to do with it as it was in life.",
3 => "
The ocean ate the last of the land and poured into the smoking gulf, thereby
giving up all it had ever conquered. From the new-flooded lands it flowed
again, uncovering death and decay; and from its ancient and immemorial bed it
trickled loathsomely, uncovering nighted secrets of the years when Time was
young and the gods unborn. Above the waves rose weedy remembered spires. The
moon laid pale lilies of light on dead London, and Paris stood up from its damp
grave to be sanctified with star-dust. Then rose spires and monoliths that were
weedy but not remembered; terrible spires and monoliths of lands that men never
knew were lands...",
4 => "
There was a night when winds from unknown spaces whirled us irresistibly into
limitless vacuum beyond all thought and entity. Perceptions of the most
maddeningly untransmissible sort thronged upon us; perceptions of infinity
which at the time convulsed us with joy, yet which are now partly lost to my
memory and partly incapable of presentation to others.",
_ => "You've met with a terrible fate, haven't you?"
};
rterrln!("{}", "");
rterrln!("{}", quote);
rterrln!("{}", "");
rterrln!("fatal runtime error: {}", msg);
unsafe { intrinsics::abort(); }
}

View File

@ -1,102 +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.
#![doc(hidden)]
use ptr;
use raw;
static RC_IMMORTAL : uint = 0x77777777;
/*
* Box annihilation
*
* This runs at task death to free all boxes.
*/
unsafe fn each_live_alloc(read_next_before: bool,
f: |alloc: *mut raw::Box<()>| -> bool)
-> bool {
//! Walks the internal list of allocations
use rt::local_heap;
let mut alloc = local_heap::live_allocs();
while alloc != ptr::mut_null() {
let next_before = (*alloc).next;
if !f(alloc) {
return false;
}
if read_next_before {
alloc = next_before;
} else {
alloc = (*alloc).next;
}
}
return true;
}
#[cfg(unix)]
fn debug_mem() -> bool {
// FIXME: Need to port the environment struct to newsched
false
}
#[cfg(windows)]
fn debug_mem() -> bool {
false
}
/// Destroys all managed memory (i.e. @ boxes) held by the current task.
pub unsafe fn annihilate() {
use rt::local_heap::local_free;
let mut n_total_boxes = 0u;
// Pass 1: Make all boxes immortal.
//
// In this pass, nothing gets freed, so it does not matter whether
// we read the next field before or after the callback.
each_live_alloc(true, |alloc| {
n_total_boxes += 1;
(*alloc).ref_count = RC_IMMORTAL;
true
});
// Pass 2: Drop all boxes.
//
// In this pass, unique-managed boxes may get freed, but not
// managed boxes, so we must read the `next` field *after* the
// callback, as the original value may have been freed.
each_live_alloc(false, |alloc| {
let drop_glue = (*alloc).drop_glue;
let data = &mut (*alloc).data as *mut ();
drop_glue(data as *mut u8);
true
});
// Pass 3: Free all boxes.
//
// In this pass, managed boxes may get freed (but not
// unique-managed boxes, though I think that none of those are
// left), so we must read the `next` field before, since it will
// not be valid after.
each_live_alloc(true, |alloc| {
local_free(alloc as *u8);
true
});
if debug_mem() {
// We do logging here w/o allocation.
println!("total boxes annihilated: {}", n_total_boxes);
}
}

102
src/libstd/failure.rs Normal file
View File

@ -0,0 +1,102 @@
// 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.
use alloc::owned::Box;
use any::{Any, AnyRefExt};
use fmt;
use io::{Writer, IoResult};
use kinds::Send;
use option::{Some, None};
use result::Ok;
use rt::backtrace;
use rt::{Stderr, Stdio};
use rustrt::local::Local;
use rustrt::task::Task;
use str::Str;
use string::String;
// Defined in this module instead of io::stdio so that the unwinding
local_data_key!(pub local_stderr: Box<Writer:Send>)
impl Writer for Stdio {
fn write(&mut self, bytes: &[u8]) -> IoResult<()> {
fn fmt_write<F: fmt::FormatWriter>(f: &mut F, bytes: &[u8]) {
let _ = f.write(bytes);
}
fmt_write(self, bytes);
Ok(())
}
}
pub fn on_fail(obj: &Any:Send, file: &'static str, line: uint) {
let msg = match obj.as_ref::<&'static str>() {
Some(s) => *s,
None => match obj.as_ref::<String>() {
Some(s) => s.as_slice(),
None => "Box<Any>",
}
};
let mut err = Stderr;
// It is assumed that all reasonable rust code will have a local task at
// all times. This means that this `exists` will return true almost all of
// the time. There are border cases, however, when the runtime has
// *almost* set up the local task, but hasn't quite gotten there yet. In
// order to get some better diagnostics, we print on failure and
// immediately abort the whole process if there is no local task
// available.
if !Local::exists(None::<Task>) {
let _ = writeln!(&mut err, "failed at '{}', {}:{}", msg, file, line);
if backtrace::log_enabled() {
let _ = backtrace::write(&mut err);
} else {
let _ = writeln!(&mut err, "run with `RUST_BACKTRACE=1` to \
see a backtrace");
}
return
}
// Peel the name out of local task so we can print it. We've got to be sure
// that the local task is in TLS while we're printing as I/O may occur.
let (name, unwinding) = {
let mut t = Local::borrow(None::<Task>);
(t.name.take(), t.unwinder.unwinding())
};
{
let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
match local_stderr.replace(None) {
Some(mut stderr) => {
// FIXME: what to do when the task printing fails?
let _ = writeln!(stderr,
"task '{}' failed at '{}', {}:{}\n",
n, msg, file, line);
if backtrace::log_enabled() {
let _ = backtrace::write(stderr);
}
local_stderr.replace(Some(stderr));
}
None => {
let _ = writeln!(&mut err, "task '{}' failed at '{}', {}:{}",
n, msg, file, line);
if backtrace::log_enabled() {
let _ = backtrace::write(&mut err);
}
}
}
// If this is a double failure, make sure that we printed a backtrace
// for this failure.
if unwinding && !backtrace::log_enabled() {
let _ = backtrace::write(&mut err);
}
}
Local::borrow(None::<Task>).name = name;
}

View File

@ -1,74 +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.
//! Implementation of running at_exit routines
//!
//! Documentation can be found on the `rt::at_exit` function.
use iter::Iterator;
use kinds::Send;
use mem;
use option::{Some, None};
use owned::Box;
use ptr::RawPtr;
use slice::OwnedVector;
use unstable::sync::Exclusive;
use vec::Vec;
type Queue = Exclusive<Vec<proc():Send>>;
// You'll note that these variables are *not* atomic, and this is done on
// purpose. This module is designed to have init() called *once* in a
// single-task context, and then run() is called only once in another
// single-task context. As a result of this, only the `push` function is
// thread-safe, and it assumes that the `init` function has run previously.
static mut QUEUE: *mut Queue = 0 as *mut Queue;
static mut RUNNING: bool = false;
pub fn init() {
unsafe {
rtassert!(!RUNNING);
rtassert!(QUEUE.is_null());
let state: Box<Queue> = box Exclusive::new(vec!());
QUEUE = mem::transmute(state);
}
}
pub fn push(f: proc():Send) {
unsafe {
rtassert!(!RUNNING);
rtassert!(!QUEUE.is_null());
let state: &mut Queue = mem::transmute(QUEUE);
let mut f = Some(f);
state.with(|arr| {
arr.push(f.take_unwrap());
});
}
}
pub fn run() {
let vec = unsafe {
rtassert!(!RUNNING);
rtassert!(!QUEUE.is_null());
RUNNING = true;
let state: Box<Queue> = mem::transmute(QUEUE);
QUEUE = 0 as *mut Queue;
let mut vec = None;
state.with(|arr| {
vec = Some(mem::replace(arr, vec!()));
});
vec.take_unwrap()
};
for f in vec.move_iter() {
f();
}
}

View File

@ -242,8 +242,7 @@ mod imp {
use mem;
use option::{Some, None, Option};
use result::{Ok, Err};
use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
use uw = rt::libunwind;
use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
struct Context<'a> {
idx: int,
@ -484,6 +483,106 @@ mod imp {
}
w.write(['\n' as u8])
}
/// Unwind library interface used for backtraces
///
/// Note that the native libraries come from librustrt, not this module.
#[allow(non_camel_case_types)]
#[allow(non_snake_case_functions)]
mod uw {
use libc;
#[repr(C)]
pub enum _Unwind_Reason_Code {
_URC_NO_REASON = 0,
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
_URC_FATAL_PHASE2_ERROR = 2,
_URC_FATAL_PHASE1_ERROR = 3,
_URC_NORMAL_STOP = 4,
_URC_END_OF_STACK = 5,
_URC_HANDLER_FOUND = 6,
_URC_INSTALL_CONTEXT = 7,
_URC_CONTINUE_UNWIND = 8,
_URC_FAILURE = 9, // used only by ARM EABI
}
pub enum _Unwind_Context {}
pub type _Unwind_Trace_Fn =
extern fn(ctx: *_Unwind_Context,
arg: *libc::c_void) -> _Unwind_Reason_Code;
extern {
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
trace_argument: *libc::c_void)
-> _Unwind_Reason_Code;
#[cfg(not(target_os = "android"),
not(target_os = "linux", target_arch = "arm"))]
pub fn _Unwind_GetIP(ctx: *_Unwind_Context) -> libc::uintptr_t;
#[cfg(not(target_os = "android"),
not(target_os = "linux", target_arch = "arm"))]
pub fn _Unwind_FindEnclosingFunction(pc: *libc::c_void)
-> *libc::c_void;
}
// On android, the function _Unwind_GetIP is a macro, and this is the
// expansion of the macro. This is all copy/pasted directly from the
// header file with the definition of _Unwind_GetIP.
#[cfg(target_os = "android")]
#[cfg(target_os = "linux", target_arch = "arm")]
pub unsafe fn _Unwind_GetIP(ctx: *_Unwind_Context) -> libc::uintptr_t {
#[repr(C)]
enum _Unwind_VRS_Result {
_UVRSR_OK = 0,
_UVRSR_NOT_IMPLEMENTED = 1,
_UVRSR_FAILED = 2,
}
#[repr(C)]
enum _Unwind_VRS_RegClass {
_UVRSC_CORE = 0,
_UVRSC_VFP = 1,
_UVRSC_FPA = 2,
_UVRSC_WMMXD = 3,
_UVRSC_WMMXC = 4,
}
#[repr(C)]
enum _Unwind_VRS_DataRepresentation {
_UVRSD_UINT32 = 0,
_UVRSD_VFPX = 1,
_UVRSD_FPAX = 2,
_UVRSD_UINT64 = 3,
_UVRSD_FLOAT = 4,
_UVRSD_DOUBLE = 5,
}
type _Unwind_Word = libc::c_uint;
extern {
fn _Unwind_VRS_Get(ctx: *_Unwind_Context,
klass: _Unwind_VRS_RegClass,
word: _Unwind_Word,
repr: _Unwind_VRS_DataRepresentation,
data: *mut libc::c_void)
-> _Unwind_VRS_Result;
}
let mut val: _Unwind_Word = 0;
let ptr = &mut val as *mut _Unwind_Word;
let _ = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 15, _UVRSD_UINT32,
ptr as *mut libc::c_void);
(val & !1) as libc::uintptr_t
}
// This function also doesn't exist on android or arm/linux, so make it
// a no-op
#[cfg(target_os = "android")]
#[cfg(target_os = "linux", target_arch = "arm")]
pub unsafe fn _Unwind_FindEnclosingFunction(pc: *libc::c_void)
-> *libc::c_void
{
pc
}
}
}
/// As always, windows has something very different than unix, we mainly want

View File

@ -1,61 +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 environment settings
use from_str::from_str;
use option::{Some, None};
use os;
use str::Str;
// Note that these are all accessed without any synchronization.
// They are expected to be initialized once then left alone.
static mut MIN_STACK: uint = 2 * 1024 * 1024;
/// This default corresponds to 20M of cache per scheduler (at the default size).
static mut MAX_CACHED_STACKS: uint = 10;
static mut DEBUG_BORROW: bool = false;
pub fn init() {
unsafe {
match os::getenv("RUST_MIN_STACK") {
Some(s) => match from_str(s.as_slice()) {
Some(i) => MIN_STACK = i,
None => ()
},
None => ()
}
match os::getenv("RUST_MAX_CACHED_STACKS") {
Some(max) => {
MAX_CACHED_STACKS =
from_str(max.as_slice()).expect("expected positive \
integer in \
RUST_MAX_CACHED_STACKS")
}
None => ()
}
match os::getenv("RUST_DEBUG_BORROW") {
Some(_) => DEBUG_BORROW = true,
None => ()
}
}
}
pub fn min_stack() -> uint {
unsafe { MIN_STACK }
}
pub fn max_cached_stacks() -> uint {
unsafe { MAX_CACHED_STACKS }
}
pub fn debug_borrow() -> bool {
unsafe { DEBUG_BORROW }
}

View File

@ -54,159 +54,36 @@ Several modules in `core` are clients of `rt`:
// FIXME: this should not be here.
#![allow(missing_doc)]
use any::Any;
use kinds::Send;
use option::Option;
use owned::Box;
use result::Result;
use task::TaskOpts;
use failure;
use rustrt;
use self::task::{Task, BlockedTask};
// this is somewhat useful when a program wants to spawn a "reasonable" number
// of workers based on the constraints of the system that it's running on.
// Perhaps this shouldn't be a `pub use` though and there should be another
// method...
pub use self::util::default_sched_threads;
// Export unwinding facilities used by the failure macros
pub use self::unwind::{begin_unwind, begin_unwind_fmt};
pub use self::util::{Stdio, Stdout, Stderr};
// TODO: dox
pub use self::util::{default_sched_threads, min_stack, running_on_valgrind};
// TODO: dox
pub use alloc::{heap, libc_heap};
// Used by I/O tests
#[experimental]
pub use self::util::running_on_valgrind;
// FIXME: these probably shouldn't be public...
#[doc(hidden)]
pub mod shouldnt_be_public {
#[cfg(not(test))]
pub use super::local_ptr::native::maybe_tls_key;
#[cfg(not(windows), not(target_os = "android"))]
pub use super::local_ptr::compiled::RT_TLS_PTR;
}
// Internal macros used by the runtime.
mod macros;
/// Implementations of language-critical runtime features like @.
pub mod task;
// The EventLoop and internal synchronous I/O interface.
pub mod rtio;
// The Local trait for types that are accessible via thread-local
// or task-local storage.
pub mod local;
pub use rustrt::{task, local, mutex, exclusive, stack, args, rtio};
pub use rustrt::{Stdio, Stdout, Stderr, begin_unwind, begin_unwind_fmt};
pub use rustrt::{bookkeeping, at_exit, unwind, DEFAULT_ERROR_CODE, Runtime};
// Bindings to system threading libraries.
pub mod thread;
// The runtime configuration, read from environment variables.
pub mod env;
// The local, managed heap
pub mod local_heap;
// The runtime needs to be able to put a pointer into thread-local storage.
mod local_ptr;
// Bindings to pthread/windows thread-local storage.
mod thread_local_storage;
// Stack unwinding
pub mod unwind;
// The interface to libunwind that rust is using.
mod libunwind;
// Simple backtrace functionality (to print on failure)
pub mod backtrace;
// Just stuff
mod util;
// Global command line argument storage
pub mod args;
// Support for running procedures when a program has exited.
mod at_exit_imp;
// Bookkeeping for task counts
pub mod bookkeeping;
// Stack overflow protection
pub mod stack;
/// The default error code of the rust runtime if the main task fails instead
/// of exiting cleanly.
pub static DEFAULT_ERROR_CODE: int = 101;
/// The interface to the current runtime.
///
/// This trait is used as the abstraction between 1:1 and M:N scheduling. The
/// two independent crates, libnative and libgreen, both have objects which
/// implement this trait. The goal of this trait is to encompass all the
/// fundamental differences in functionality between the 1:1 and M:N runtime
/// modes.
pub trait Runtime {
// Necessary scheduling functions, used for channels and blocking I/O
// (sometimes).
fn yield_now(~self, cur_task: Box<Task>);
fn maybe_yield(~self, cur_task: Box<Task>);
fn deschedule(~self, times: uint, cur_task: Box<Task>,
f: |BlockedTask| -> Result<(), BlockedTask>);
fn reawaken(~self, to_wake: Box<Task>);
// Miscellaneous calls which are very different depending on what context
// you're in.
fn spawn_sibling(~self,
cur_task: Box<Task>,
opts: TaskOpts,
f: proc():Send);
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
/// The (low, high) edges of the current stack.
fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
fn can_block(&self) -> bool;
// FIXME: This is a serious code smell and this should not exist at all.
fn wrap(~self) -> Box<Any>;
}
/// One-time runtime initialization.
///
/// Initializes global state, including frobbing
/// the crate's logging flags, registering GC
/// metadata, and storing the process arguments.
#[allow(experimental)]
pub fn init(argc: int, argv: **u8) {
// FIXME: Derefing these pointers is not safe.
// Need to propagate the unsafety to `start`.
unsafe {
args::init(argc, argv);
env::init();
local_ptr::init();
at_exit_imp::init();
}
}
/// 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 tasks have exited.
///
/// The procedure is *not* executed with a local `Task` 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
/// are running, and doing so will lead to a process abort.
pub fn at_exit(f: proc():Send) {
at_exit_imp::push(f);
rustrt::init(argc, argv);
unsafe { unwind::register(failure::on_fail); }
}
/// One-time runtime cleanup.
@ -219,8 +96,5 @@ pub fn at_exit(f: proc():Send) {
/// Invoking cleanup while portions of the runtime are still in use may cause
/// undefined behavior.
pub unsafe fn cleanup() {
bookkeeping::wait_for_other_tasks();
at_exit_imp::run();
args::cleanup();
local_ptr::cleanup();
rustrt::cleanup();
}

View File

@ -8,23 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use container::Container;
use fmt;
use from_str::FromStr;
use io::IoResult;
use io;
use iter::Iterator;
use libc;
use from_str::from_str;
use libc::uintptr_t;
use libc;
use option::{Some, None, Option};
use os;
use result::Ok;
use str::{Str, StrSlice};
use slice::ImmutableVector;
// 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 static ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || cfg!(rtassert);
use str::Str;
use sync::atomics;
/// Get the number of cores available
pub fn num_cpus() -> uint {
@ -37,6 +28,17 @@ pub fn num_cpus() -> uint {
}
}
/// Dynamically inquire about whether we're running under V.
/// You should usually not use this unless your test definitely
/// can't run correctly un-altered. Valgrind is there to help
/// you notice weirdness in normal, un-doctored code paths!
pub fn running_on_valgrind() -> bool {
extern {
fn rust_running_on_valgrind() -> uintptr_t;
}
unsafe { rust_running_on_valgrind() != 0 }
}
/// Valgrind has a fixed-sized array (size around 2000) of segment descriptors
/// wired into it; this is a hard limit and requires rebuilding valgrind if you
/// want to go beyond it. Normally this is not a problem, but in some tests, we
@ -50,6 +52,20 @@ pub fn limit_thread_creation_due_to_osx_and_valgrind() -> bool {
(cfg!(target_os="macos")) && running_on_valgrind()
}
pub fn min_stack() -> uint {
static mut MIN: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
match unsafe { MIN.load(atomics::SeqCst) } {
0 => {}
n => return n - 1,
}
let amt = os::getenv("RUST_MIN_STACK").and_then(|s| from_str(s.as_slice()));
let amt = amt.unwrap_or(2 * 1024 * 1024);
// 0 is our sentinel value, so ensure that we'll never see 0 after
// initialization has run
unsafe { MIN.store(amt + 1, atomics::SeqCst); }
return amt;
}
/// Get's the number of scheduler threads requested by the environment
/// either `RUST_THREADS` or `num_cpus`.
pub fn default_sched_threads() -> uint {
@ -58,7 +74,7 @@ pub fn default_sched_threads() -> uint {
let opt_n: Option<uint> = FromStr::from_str(nstr.as_slice());
match opt_n {
Some(n) if n > 0 => n,
_ => rtabort!("`RUST_THREADS` is `{}`, should be a positive integer", nstr)
_ => fail!("`RUST_THREADS` is `{}`, should be a positive integer", nstr)
}
}
None => {
@ -70,107 +86,3 @@ pub fn default_sched_threads() -> uint {
}
}
}
pub struct Stdio(libc::c_int);
pub static Stdout: Stdio = Stdio(libc::STDOUT_FILENO);
pub static Stderr: Stdio = Stdio(libc::STDERR_FILENO);
impl io::Writer for Stdio {
fn write(&mut self, data: &[u8]) -> IoResult<()> {
#[cfg(unix)]
type WriteLen = libc::size_t;
#[cfg(windows)]
type WriteLen = libc::c_uint;
unsafe {
let Stdio(fd) = *self;
libc::write(fd,
data.as_ptr() as *libc::c_void,
data.len() as WriteLen);
}
Ok(()) // yes, we're lying
}
}
pub fn dumb_println(args: &fmt::Arguments) {
use io::Writer;
let mut w = Stderr;
let _ = writeln!(&mut w, "{}", args);
}
pub fn abort(msg: &str) -> ! {
let msg = if !msg.is_empty() { msg } else { "aborted" };
let hash = msg.chars().fold(0, |accum, val| accum + (val as uint) );
let quote = match hash % 10 {
0 => "
It was from the artists and poets that the pertinent answers came, and I
know that panic would have broken loose had they been able to compare notes.
As it was, lacking their original letters, I half suspected the compiler of
having asked leading questions, or of having edited the correspondence in
corroboration of what he had latently resolved to see.",
1 => "
There are not many persons who know what wonders are opened to them in the
stories and visions of their youth; for when as children we listen and dream,
we think but half-formed thoughts, and when as men we try to remember, we are
dulled and prosaic with the poison of life. But some of us awake in the night
with strange phantasms of enchanted hills and gardens, of fountains that sing
in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch
down to sleeping cities of bronze and stone, and of shadowy companies of heroes
that ride caparisoned white horses along the edges of thick forests; and then
we know that we have looked back through the ivory gates into that world of
wonder which was ours before we were wise and unhappy.",
2 => "
Instead of the poems I had hoped for, there came only a shuddering blackness
and ineffable loneliness; and I saw at last a fearful truth which no one had
ever dared to breathe before the unwhisperable secret of secrets The fact
that this city of stone and stridor is not a sentient perpetuation of Old New
York as London is of Old London and Paris of Old Paris, but that it is in fact
quite dead, its sprawling body imperfectly embalmed and infested with queer
animate things which have nothing to do with it as it was in life.",
3 => "
The ocean ate the last of the land and poured into the smoking gulf, thereby
giving up all it had ever conquered. From the new-flooded lands it flowed
again, uncovering death and decay; and from its ancient and immemorial bed it
trickled loathsomely, uncovering nighted secrets of the years when Time was
young and the gods unborn. Above the waves rose weedy remembered spires. The
moon laid pale lilies of light on dead London, and Paris stood up from its damp
grave to be sanctified with star-dust. Then rose spires and monoliths that were
weedy but not remembered; terrible spires and monoliths of lands that men never
knew were lands...",
4 => "
There was a night when winds from unknown spaces whirled us irresistibly into
limitless vacuum beyond all thought and entity. Perceptions of the most
maddeningly untransmissible sort thronged upon us; perceptions of infinity
which at the time convulsed us with joy, yet which are now partly lost to my
memory and partly incapable of presentation to others.",
_ => "You've met with a terrible fate, haven't you?"
};
::alloc::util::make_stdlib_link_work(); // see comments in liballoc
rterrln!("{}", "");
rterrln!("{}", quote);
rterrln!("{}", "");
rterrln!("fatal runtime error: {}", msg);
{
let mut err = Stderr;
let _err = ::rt::backtrace::write(&mut err);
}
abort();
fn abort() -> ! {
use intrinsics;
unsafe { intrinsics::abort() }
}
}
/// Dynamically inquire about whether we're running under V.
/// You should usually not use this unless your test definitely
/// can't run correctly un-altered. Valgrind is there to help
/// you notice weirdness in normal, un-doctored code paths!
pub fn running_on_valgrind() -> bool {
unsafe { rust_running_on_valgrind() != 0 }
}
extern {
fn rust_running_on_valgrind() -> uintptr_t;
}

View File

@ -1,159 +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.
use alloc::arc::Arc;
use clone::Clone;
use kinds::Send;
use ty::Unsafe;
use unstable::mutex::NativeMutex;
struct ExData<T> {
lock: NativeMutex,
failed: bool,
data: T,
}
/**
* An arc over mutable data that is protected by a lock. For library use only.
*
* # Safety note
*
* This uses a pthread mutex, not one that's aware of the userspace scheduler.
* The user of an Exclusive must be careful not to invoke any functions that may
* reschedule the task while holding the lock, or deadlock may result. If you
* need to block or deschedule while accessing shared state, use extra::sync::RWArc.
*/
pub struct Exclusive<T> {
x: Arc<Unsafe<ExData<T>>>
}
impl<T:Send> Clone for Exclusive<T> {
// Duplicate an Exclusive Arc, as std::arc::clone.
fn clone(&self) -> Exclusive<T> {
Exclusive { x: self.x.clone() }
}
}
impl<T:Send> Exclusive<T> {
pub fn new(user_data: T) -> Exclusive<T> {
let data = ExData {
lock: unsafe {NativeMutex::new()},
failed: false,
data: user_data
};
Exclusive {
x: Arc::new(Unsafe::new(data))
}
}
// Exactly like sync::MutexArc.access(). Same reason for being
// unsafe.
//
// Currently, scheduling operations (i.e., descheduling, receiving on a pipe,
// accessing the provided condition variable) are prohibited while inside
// the Exclusive. Supporting that is a work in progress.
#[inline]
pub unsafe fn with<U>(&self, f: |x: &mut T| -> U) -> U {
let rec = self.x.get();
let _l = (*rec).lock.lock();
if (*rec).failed {
fail!("Poisoned Exclusive::new - another task failed inside!");
}
(*rec).failed = true;
let result = f(&mut (*rec).data);
(*rec).failed = false;
result
}
#[inline]
pub unsafe fn with_imm<U>(&self, f: |x: &T| -> U) -> U {
self.with(|x| f(x))
}
#[inline]
pub unsafe fn hold_and_signal(&self, f: |x: &mut T|) {
let rec = self.x.get();
let guard = (*rec).lock.lock();
if (*rec).failed {
fail!("Poisoned Exclusive::new - another task failed inside!");
}
(*rec).failed = true;
f(&mut (*rec).data);
(*rec).failed = false;
guard.signal();
}
#[inline]
pub unsafe fn hold_and_wait(&self, f: |x: &T| -> bool) {
let rec = self.x.get();
let l = (*rec).lock.lock();
if (*rec).failed {
fail!("Poisoned Exclusive::new - another task failed inside!");
}
(*rec).failed = true;
let result = f(&(*rec).data);
(*rec).failed = false;
if result {
l.wait();
}
}
}
#[cfg(test)]
mod tests {
use option::*;
use prelude::*;
use super::Exclusive;
use task;
#[test]
fn exclusive_new_arc() {
unsafe {
let mut futures = Vec::new();
let num_tasks = 10;
let count = 10;
let total = Exclusive::new(box 0);
for _ in range(0u, num_tasks) {
let total = total.clone();
let (tx, rx) = channel();
futures.push(rx);
task::spawn(proc() {
for _ in range(0u, count) {
total.with(|count| **count += 1);
}
tx.send(());
});
};
for f in futures.mut_iter() { f.recv() }
total.with(|total| assert!(**total == num_tasks * count));
}
}
#[test] #[should_fail]
fn exclusive_new_poison() {
unsafe {
// Tests that if one task fails inside of an Exclusive::new, subsequent
// accesses will also fail.
let x = Exclusive::new(1);
let x2 = x.clone();
let _ = task::try(proc() {
x2.with(|one| assert_eq!(*one, 2))
});
x.with(|one| assert_eq!(*one, 1));
}
}
}