diff --git a/mk/crates.mk b/mk/crates.mk index 97bf7db5332..433490f6f5d 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 diff --git a/mk/rt.mk b/mk/rt.mk index 625e1a8d0c7..a75cb8aa4de 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -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 diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 94901aff001..7942a1569ed 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -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(); diff --git a/src/libstd/rt/args.rs b/src/librustrt/args.rs similarity index 85% rename from src/libstd/rt/args.rs rename to src/librustrt/args.rs index e016c9da418..0789bf7f906 100644 --- a/src/libstd/rt/args.rs +++ b/src/librustrt/args.rs @@ -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>> { 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> { - 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) { } diff --git a/src/librustrt/at_exit_imp.rs b/src/librustrt/at_exit_imp.rs new file mode 100644 index 00000000000..d38d06950bf --- /dev/null +++ b/src/librustrt/at_exit_imp.rs @@ -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 or the MIT license +// , 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>; + +static mut QUEUE: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; +static mut RUNNING: atomics::AtomicBool = atomics::INIT_ATOMIC_BOOL; + +pub fn init() { + let state: Box = 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 = mem::transmute(queue); + mem::replace(&mut *queue.lock(), Vec::new()) + }; + + for to_run in cur.move_iter() { + to_run(); + } +} diff --git a/src/libstd/rt/bookkeeping.rs b/src/librustrt/bookkeeping.rs similarity index 91% rename from src/libstd/rt/bookkeeping.rs rename to src/librustrt/bookkeeping.rs index 9e772d8ad23..fd290491eaf 100644 --- a/src/libstd/rt/bookkeeping.rs +++ b/src/librustrt/bookkeeping.rs @@ -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; diff --git a/src/libstd/c_str.rs b/src/librustrt/c_str.rs similarity index 97% rename from src/libstd/c_str.rs rename to src/librustrt/c_str.rs index 4e39518deb4..1690c54285c 100644 --- a/src/libstd/c_str.rs +++ b/src/librustrt/c_str.rs @@ -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) { diff --git a/src/librustrt/exclusive.rs b/src/librustrt/exclusive.rs new file mode 100644 index 00000000000..62313965768 --- /dev/null +++ b/src/librustrt/exclusive.rs @@ -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 or the MIT license +// , 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 { + lock: mutex::NativeMutex, + data: Unsafe, +} + +/// 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 Exclusive { + /// Creates a new `Exclusive` which will protect the data provided. + pub fn new(user_data: T) -> Exclusive { + 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 for ExclusiveGuard<'a, T> { + fn deref<'a>(&'a self) -> &'a T { &*self._data } +} +impl<'a, T: Send> DerefMut 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); + } + } +} diff --git a/src/librustrt/lib.rs b/src/librustrt/lib.rs new file mode 100644 index 00000000000..506df10aab2 --- /dev/null +++ b/src/librustrt/lib.rs @@ -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 or the MIT license +// , 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); + fn maybe_yield(~self, cur_task: Box); + fn deschedule(~self, times: uint, cur_task: Box, + f: |BlockedTask| -> Result<(), BlockedTask>); + fn reawaken(~self, to_wake: Box); + + // Miscellaneous calls which are very different depending on what context + // you're in. + fn spawn_sibling(~self, + cur_task: Box, + opts: TaskOpts, + f: proc():Send); + fn local_io<'a>(&'a mut self) -> Option>; + /// 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; +} + +/// 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}; +} diff --git a/src/libstd/rt/libunwind.rs b/src/librustrt/libunwind.rs similarity index 52% rename from src/libstd/rt/libunwind.rs rename to src/librustrt/libunwind.rs index 00301e71b0d..846ec248805 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/librustrt/libunwind.rs @@ -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 } diff --git a/src/libstd/rt/local.rs b/src/librustrt/local.rs similarity index 95% rename from src/libstd/rt/local.rs rename to src/librustrt/local.rs index 9f0ed804480..7fe9dbc6d4f 100644 --- a/src/libstd/rt/local.rs +++ b/src/librustrt/local.rs @@ -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 { @@ -52,11 +53,10 @@ impl Local> 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() { diff --git a/src/libstd/local_data.rs b/src/librustrt/local_data.rs similarity index 96% rename from src/libstd/local_data.rs rename to src/librustrt/local_data.rs index 930d1df02f1..88c154045c6 100644 --- a/src/libstd/local_data.rs +++ b/src/librustrt/local_data.rs @@ -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 KeyValue { 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 Drop for Ref { #[cfg(test)] mod tests { - use prelude::*; + use std::prelude::*; use super::*; - use owned::Box; - use task; + use std::task; #[test] fn test_tls_multitask() { diff --git a/src/libstd/rt/local_heap.rs b/src/librustrt/local_heap.rs similarity index 75% rename from src/libstd/rt/local_heap.rs rename to src/librustrt/local_heap.rs index 240d137adc6..52fe5c35a26 100644 --- a/src/libstd/rt/local_heap.rs +++ b/src/librustrt/local_heap.rs @@ -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::).heap.live_allocs -} - #[cfg(test)] mod bench { extern crate test; diff --git a/src/libstd/rt/local_ptr.rs b/src/librustrt/local_ptr.rs similarity index 96% rename from src/libstd/rt/local_ptr.rs rename to src/librustrt/local_ptr.rs index 1197a4ccbe6..91e3409892e 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/librustrt/local_ptr.rs @@ -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() -> Borrowed { /// 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 { - use realstd; + use realrustrt; unsafe { - mem::transmute(realstd::rt::shouldnt_be_public::maybe_tls_key()) + mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key()) } } } diff --git a/src/libstd/rt/macros.rs b/src/librustrt/macros.rs similarity index 82% rename from src/libstd/rt/macros.rs rename to src/librustrt/macros.rs index aef41de925f..d4e92736a9d 100644 --- a/src/libstd/rt/macros.rs +++ b/src/librustrt/macros.rs @@ -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)*)) ) diff --git a/src/libstd/unstable/mutex.rs b/src/librustrt/mutex.rs similarity index 97% rename from src/libstd/unstable/mutex.rs rename to src/librustrt/mutex.rs index 4e51e714777..fccbe4a15e9 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/librustrt/mutex.rs @@ -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() { diff --git a/src/libstd/rt/rtio.rs b/src/librustrt/rtio.rs similarity index 82% rename from src/libstd/rt/rtio.rs rename to src/librustrt/rtio.rs index 4b5ca6178a7..00f761bae21 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/librustrt/rtio.rs @@ -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(f: |io: &mut IoFactory| -> IoResult) -> IoResult { + #[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) -> IoResult>; fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>, - hint: Option) -> IoResult>; + hint: Option) + -> IoResult>; // filesystem operations fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior) @@ -215,10 +209,8 @@ pub trait IoFactory { -> IoResult>; fn fs_unlink(&mut self, path: &CString) -> IoResult<()>; fn fs_stat(&mut self, path: &CString) -> IoResult; - 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>; fn tty_open(&mut self, fd: c_int, readable: bool) -> IoResult>; - fn signal(&mut self, signal: Signum, channel: Sender) + fn signal(&mut self, signal: int, cb: Box) -> IoResult>; } @@ -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); + fn period(&mut self, msecs: u64, cb: Box); } 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, +} + +pub type IoResult = Result; + +#[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, +} diff --git a/src/libstd/rt/stack.rs b/src/librustrt/stack.rs similarity index 98% rename from src/libstd/rt/stack.rs rename to src/librustrt/stack.rs index dc6ab494d64..148d93adc84 100644 --- a/src/libstd/rt/stack.rs +++ b/src/librustrt/stack.rs @@ -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 diff --git a/src/libstd/rt/task.rs b/src/librustrt/task.rs similarity index 85% rename from src/libstd/rt/task.rs rename to src/librustrt/task.rs index 7f492a00b80..f56b891ed9f 100644 --- a/src/libstd/rt/task.rs +++ b/src/librustrt/task.rs @@ -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, - pub stdout: Option>, - pub stderr: Option>, - imp: Option>, } +pub struct TaskOpts { + /// Invoke this procedure with the result of the task when it finishes. + pub on_exit: Option, + /// A name for the task-to-be, for identification in failure messages + pub name: Option, + /// The size of the stack for the spawned task + pub stack_size: Option, +} + +/// 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>; + pub struct GarbageCollector; pub struct LocalStorage(pub Option); @@ -69,17 +76,9 @@ pub enum BlockedTask { Shared(Arc), } -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), -} - /// Per-task state related to task death, killing, failure, etc. pub struct Death { - pub on_exit: Option, + pub on_exit: Option, } 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::); - 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::); + 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 for BlockedTasks { fn next(&mut self) -> Option { 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>, @@ -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!()) } diff --git a/src/libstd/rt/thread_local_storage.rs b/src/librustrt/thread_local_storage.rs similarity index 72% rename from src/libstd/rt/thread_local_storage.rs rename to src/librustrt/thread_local_storage.rs index a3ebcbafff8..2cdeb21fb83 100644 --- a/src/libstd/rt/thread_local_storage.rs +++ b/src/librustrt/thread_local_storage.rs @@ -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 = transmute(get(key)); - assert_eq!(value, box 20); - let value = box 30; - set(key, transmute(value)); - let value: Box = 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 = transmute(get(key)); + assert_eq!(value, box 20); + let value = box 30; + set(key, transmute(value)); + let value: Box = transmute(get(key)); + assert_eq!(value, box 30); + } } } diff --git a/src/libstd/rt/unwind.rs b/src/librustrt/unwind.rs similarity index 60% rename from src/libstd/rt/unwind.rs rename to src/librustrt/unwind.rs index af73e6167af..da6fd3644ce 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/librustrt/unwind.rs @@ -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>, } +pub type Callback = fn(msg: &Any:Send, file: &'static str, line: uint); +type Queue = Exclusive>; + +// 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> { } } + #[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 } + + 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(msg: M, file: &'static str, line: uint) -> ! fn begin_unwind_inner(msg: Box, file: &'static str, line: uint) -> ! { - // First up, print the message that we're failing - print_failure(msg, file, line); - - let opt_task: Option> = 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 = 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::() { - Some(s) => s.as_slice(), - None => "Box", - } - }; - - // 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 = 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(""); - - 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 + } + } +} diff --git a/src/librustrt/util.rs b/src/librustrt/util.rs new file mode 100644 index 00000000000..0b517bcc7fb --- /dev/null +++ b/src/librustrt/util.rs @@ -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 or the MIT license +// , 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(); } +} diff --git a/src/libstd/cleanup.rs b/src/libstd/cleanup.rs deleted file mode 100644 index 2e51931f15a..00000000000 --- a/src/libstd/cleanup.rs +++ /dev/null @@ -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 or the MIT license -// , 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); - } -} diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs new file mode 100644 index 00000000000..903f39c7b06 --- /dev/null +++ b/src/libstd/failure.rs @@ -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 or the MIT license +// , 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) + +impl Writer for Stdio { + fn write(&mut self, bytes: &[u8]) -> IoResult<()> { + fn fmt_write(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::() { + Some(s) => s.as_slice(), + None => "Box", + } + }; + 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::) { + 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::); + (t.name.take(), t.unwinder.unwinding()) + }; + { + let n = name.as_ref().map(|n| n.as_slice()).unwrap_or(""); + + 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::).name = name; +} diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs deleted file mode 100644 index c892a73d934..00000000000 --- a/src/libstd/rt/at_exit_imp.rs +++ /dev/null @@ -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 or the MIT license -// , 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>; - -// 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 = 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 = 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(); - } -} diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 94472ee7241..4229e9ad32c 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -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 diff --git a/src/libstd/rt/env.rs b/src/libstd/rt/env.rs deleted file mode 100644 index 7271464d1e9..00000000000 --- a/src/libstd/rt/env.rs +++ /dev/null @@ -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 or the MIT license -// , 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 } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index d2131ad44fb..e8df7465a74 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -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); - fn maybe_yield(~self, cur_task: Box); - fn deschedule(~self, times: uint, cur_task: Box, - f: |BlockedTask| -> Result<(), BlockedTask>); - fn reawaken(~self, to_wake: Box); - - // Miscellaneous calls which are very different depending on what context - // you're in. - fn spawn_sibling(~self, - cur_task: Box, - opts: TaskOpts, - f: proc():Send); - fn local_io<'a>(&'a mut self) -> Option>; - /// 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; -} - /// 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(); } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 103fbdc0bc9..670d4aa2061 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -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 = 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; -} diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs deleted file mode 100644 index f0f7e40ce09..00000000000 --- a/src/libstd/unstable/sync.rs +++ /dev/null @@ -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 or the MIT license -// , 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 { - 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 { - x: Arc>> -} - -impl Clone for Exclusive { - // Duplicate an Exclusive Arc, as std::arc::clone. - fn clone(&self) -> Exclusive { - Exclusive { x: self.x.clone() } - } -} - -impl Exclusive { - pub fn new(user_data: T) -> Exclusive { - 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(&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(&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)); - } - } -}