From 2b3477d373603527d23cc578f3737857b7b253d7 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 23 Nov 2014 19:21:17 -0800 Subject: [PATCH 01/24] libs: merge librustrt into libstd This commit merges the `rustrt` crate into `std`, undoing part of the facade. This merger continues the paring down of the runtime system. Code relying on the public API of `rustrt` will break; some of this API is now available through `std::rt`, but is likely to change and/or be removed very soon. [breaking-change] --- mk/crates.mk | 7 +- src/libcollections/slice.rs | 12 +- src/librustrt/util.rs | 136 --- src/{librustrt => libstd}/c_str.rs | 30 +- src/libstd/comm/mod.rs | 11 +- src/libstd/comm/oneshot.rs | 4 +- src/libstd/comm/select.rs | 4 +- src/libstd/comm/shared.rs | 6 +- src/libstd/comm/stream.rs | 6 +- src/libstd/comm/sync.rs | 6 +- src/libstd/failure.rs | 6 +- src/libstd/io/stdio.rs | 7 +- src/libstd/lib.rs | 9 +- src/libstd/os.rs | 4 +- src/{librustrt => libstd/rt}/args.rs | 14 +- src/{librustrt => libstd/rt}/at_exit_imp.rs | 12 +- src/libstd/rt/backtrace.rs | 978 +----------------- src/{librustrt => libstd/rt}/bookkeeping.rs | 6 +- src/{librustrt => libstd/rt}/exclusive.rs | 10 +- src/{librustrt => libstd/rt}/libunwind.rs | 0 src/{librustrt => libstd/rt}/local.rs | 12 +- src/{librustrt => libstd/rt}/local_ptr.rs | 22 +- src/{librustrt => libstd/rt}/macros.rs | 7 +- src/libstd/rt/mod.rs | 88 +- src/{librustrt => libstd/rt}/mutex.rs | 337 +----- src/{librustrt => libstd/rt}/task.rs | 48 +- src/libstd/rt/thread.rs | 171 +++ .../rt}/thread_local_storage.rs | 6 +- src/{librustrt => libstd/rt}/unwind.rs | 39 +- src/libstd/rt/util.rs | 147 ++- src/libstd/rtdeps.rs | 2 +- src/libstd/sys/common/backtrace.rs | 131 +++ src/libstd/sys/common/helper_thread.rs | 5 +- src/libstd/sys/common/mod.rs | 3 + src/{librustrt => libstd/sys/common}/stack.rs | 4 +- src/libstd/sys/common/thread.rs | 34 + src/libstd/sys/common/thread_local.rs | 4 +- src/libstd/sys/unix/backtrace.rs | 493 +++++++++ src/libstd/sys/unix/mod.rs | 3 + .../sys/unix}/stack_overflow.rs | 166 +-- src/libstd/sys/unix/thread.rs | 270 +++++ src/libstd/sys/windows/backtrace.rs | 371 +++++++ src/libstd/sys/windows/mod.rs | 3 + src/libstd/sys/windows/stack_overflow.rs | 120 +++ src/libstd/sys/windows/thread.rs | 95 ++ src/libstd/sys/windows/thread_local.rs | 6 +- src/libstd/task.rs | 12 +- src/libstd/thread_local/mod.rs | 2 +- src/{librustrt => libstd}/thunk.rs | 0 src/rt/rust_try.ll | 2 +- src/test/run-pass/foreign-call-no-runtime.rs | 3 +- .../match-ref-binding-in-guard-3256.rs | 4 +- src/test/run-pass/running-with-no-runtime.rs | 4 +- src/test/run-pass/writealias.rs | 4 +- 54 files changed, 2111 insertions(+), 1775 deletions(-) delete mode 100644 src/librustrt/util.rs rename src/{librustrt => libstd}/c_str.rs (98%) rename src/{librustrt => libstd/rt}/args.rs (95%) rename src/{librustrt => libstd/rt}/at_exit_imp.rs (93%) rename src/{librustrt => libstd/rt}/bookkeeping.rs (95%) rename src/{librustrt => libstd/rt}/exclusive.rs (96%) rename src/{librustrt => libstd/rt}/libunwind.rs (100%) rename src/{librustrt => libstd/rt}/local.rs (96%) rename src/{librustrt => libstd/rt}/local_ptr.rs (97%) rename src/{librustrt => libstd/rt}/macros.rs (86%) rename src/{librustrt => libstd/rt}/mutex.rs (52%) rename src/{librustrt => libstd/rt}/task.rs (96%) create mode 100644 src/libstd/rt/thread.rs rename src/{librustrt => libstd/rt}/thread_local_storage.rs (96%) rename src/{librustrt => libstd/rt}/unwind.rs (98%) create mode 100644 src/libstd/sys/common/backtrace.rs rename src/{librustrt => libstd/sys/common}/stack.rs (99%) create mode 100644 src/libstd/sys/common/thread.rs create mode 100644 src/libstd/sys/unix/backtrace.rs rename src/{librustrt => libstd/sys/unix}/stack_overflow.rs (64%) create mode 100644 src/libstd/sys/unix/thread.rs create mode 100644 src/libstd/sys/windows/backtrace.rs create mode 100644 src/libstd/sys/windows/stack_overflow.rs create mode 100644 src/libstd/sys/windows/thread.rs rename src/{librustrt => libstd}/thunk.rs (100%) diff --git a/mk/crates.mk b/mk/crates.mk index a47d4871326df..fafe77c78da4e 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -51,7 +51,7 @@ TARGET_CRATES := libc std flate arena term \ serialize getopts collections test time rand \ - log regex graphviz core rbml alloc rustrt \ + log regex graphviz core rbml alloc \ unicode RUSTC_CRATES := rustc rustc_typeck rustc_borrowck rustc_driver rustc_trans rustc_back rustc_llvm HOST_CRATES := syntax $(RUSTC_CRATES) rustdoc regex_macros fmt_macros @@ -62,9 +62,8 @@ DEPS_core := DEPS_libc := core DEPS_unicode := core DEPS_alloc := core libc native:jemalloc -DEPS_rustrt := alloc core libc collections native:rustrt_native -DEPS_std := core libc rand alloc collections rustrt unicode \ - native:rust_builtin native:backtrace +DEPS_std := core libc rand alloc collections unicode \ + native:rust_builtin native:backtrace native:rustrt_native DEPS_graphviz := std DEPS_syntax := std term serialize log fmt_macros arena libc DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index bba00a80f6854..c2ed28d81df72 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1344,8 +1344,6 @@ pub mod raw { #[cfg(test)] mod tests { - extern crate rustrt; - use std::cell::Cell; use std::default::Default; use std::mem; @@ -1629,9 +1627,9 @@ mod tests { #[test] fn test_swap_remove_noncopyable() { // Tests that we don't accidentally run destructors twice. - let mut v = vec![rustrt::exclusive::Exclusive::new(()), - rustrt::exclusive::Exclusive::new(()), - rustrt::exclusive::Exclusive::new(())]; + let mut v = vec![rt::exclusive::Exclusive::new(()), + rt::exclusive::Exclusive::new(()), + rt::exclusive::Exclusive::new(())]; let mut _e = v.swap_remove(0); assert_eq!(v.len(), 2); _e = v.swap_remove(1); @@ -1736,7 +1734,7 @@ mod tests { v2.dedup(); /* * If the boxed pointers were leaked or otherwise misused, valgrind - * and/or rustrt should raise errors. + * and/or rt should raise errors. */ } @@ -1750,7 +1748,7 @@ mod tests { v2.dedup(); /* * If the pointers were leaked or otherwise misused, valgrind and/or - * rustrt should raise errors. + * rt should raise errors. */ } diff --git a/src/librustrt/util.rs b/src/librustrt/util.rs deleted file mode 100644 index 74f918b8ee3b4..0000000000000 --- a/src/librustrt/util.rs +++ /dev/null @@ -1,136 +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. -// -// ignore-lexer-test FIXME #15677 - -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 const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || - cfg!(rtassert); - -pub struct Stdio(libc::c_int); - -#[allow(non_upper_case_globals)] -impl Copy for Stdio {} - -#[allow(non_upper_case_globals)] -pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); -#[allow(non_upper_case_globals)] -pub const 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 *const 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 self.pos..]; - let to_write = bytes[..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: &mut msg, pos: 0 }; - let _ = write!(&mut w, "{}", args); - let msg = str::from_utf8(w.buf[mut ..w.pos]).unwrap_or("aborted"); - let msg = if msg.is_empty() {"aborted"} else {msg}; - - // 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/librustrt/c_str.rs b/src/libstd/c_str.rs similarity index 98% rename from src/librustrt/c_str.rs rename to src/libstd/c_str.rs index 6649b93e4b057..27a139835c980 100644 --- a/src/librustrt/c_str.rs +++ b/src/libstd/c_str.rs @@ -67,17 +67,17 @@ //! } //! ``` +use string::String; +use hash; +use fmt; +use kinds::marker; +use mem; use core::prelude::*; -use collections::string::String; -use core::hash; -use core::fmt; -use core::kinds::{Sized, marker}; -use core::mem; -use core::ptr; -use core::raw::Slice; -use core::slice; -use core::str; +use ptr; +use raw::Slice; +use slice; +use str; use libc; /// The representation of a C String. @@ -534,9 +534,9 @@ pub unsafe fn from_c_multistring(buf: *const libc::c_char, #[cfg(test)] mod tests { - use std::prelude::*; - use std::ptr; - use std::task; + use prelude::*; + use ptr; + use task; use libc; use super::*; @@ -726,9 +726,11 @@ mod tests { #[cfg(test)] mod bench { - use test::Bencher; + extern crate test; + + use self::test::Bencher; use libc; - use std::prelude::*; + use prelude::*; #[inline] fn check(s: &str, c_str: *const libc::c_char) { diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 29a7b0dd0cc54..dfbb09d26b5b4 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -327,7 +327,7 @@ use alloc::arc::Arc; use core::kinds::marker; use core::mem; use core::cell::UnsafeCell; -use rustrt::task::BlockedTask; +use rt::task::BlockedTask; pub use comm::select::{Select, Handle}; @@ -336,9 +336,8 @@ macro_rules! test { mod $name { #![allow(unused_imports)] - extern crate rustrt; - use prelude::*; + use rt; use comm::*; use super::*; @@ -1519,7 +1518,7 @@ mod test { } } test! { fn sends_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = channel(); let t = Thread::start(move|| { @@ -1534,7 +1533,7 @@ mod test { } } test! { fn try_recvs_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = channel(); let (cdone, pdone) = channel(); @@ -1984,7 +1983,7 @@ mod sync_tests { } } test! { fn try_recvs_off_the_runtime() { - use rustrt::thread::Thread; + use rt::thread::Thread; let (tx, rx) = sync_channel::<()>(0); let (cdone, pdone) = channel(); diff --git a/src/libstd/comm/oneshot.rs b/src/libstd/comm/oneshot.rs index bc34c3e8c5295..2c5248c0897b2 100644 --- a/src/libstd/comm/oneshot.rs +++ b/src/libstd/comm/oneshot.rs @@ -41,8 +41,8 @@ use core::prelude::*; use alloc::boxed::Box; use core::mem; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; use sync::atomic; use comm::Receiver; diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index de2b84b083c23..4da9b4cfa369f 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -59,8 +59,8 @@ use core::cell::Cell; use core::kinds::marker; use core::mem; use core::uint; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; use comm::Receiver; diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 13b5e10fcd3dc..b3856e588e233 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -25,9 +25,9 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; -use rustrt::thread::Thread; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; +use rt::thread::Thread; use sync::{atomic, Mutex, MutexGuard}; use comm::mpsc_queue as mpsc; diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs index 06ab4f4427aa6..827b1d51ac48d 100644 --- a/src/libstd/comm/stream.rs +++ b/src/libstd/comm/stream.rs @@ -27,9 +27,9 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rustrt::local::Local; -use rustrt::task::{Task, BlockedTask}; -use rustrt::thread::Thread; +use rt::local::Local; +use rt::task::{Task, BlockedTask}; +use rt::thread::Thread; use sync::atomic; use comm::spsc_queue as spsc; diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index a2e839e134c7d..933cd43c662aa 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -42,9 +42,9 @@ use alloc::boxed::Box; use vec::Vec; use core::mem; use core::cell::UnsafeCell; -use rustrt::local::Local; -use rustrt::mutex::{NativeMutex, LockGuard}; -use rustrt::task::{Task, BlockedTask}; +use rt::local::Local; +use rt::mutex::{NativeMutex, LockGuard}; +use rt::task::{Task, BlockedTask}; use sync::atomic; diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 89bccb8b99fe8..5438f1920d6f9 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -20,9 +20,9 @@ use option::Option; use option::Option::{Some, None}; use result::Result::Ok; use rt::backtrace; -use rustrt::{Stderr, Stdio}; -use rustrt::local::Local; -use rustrt::task::Task; +use rt::util::{Stderr, Stdio}; +use rt::local::Local; +use rt::task::Task; use str::Str; use string::String; diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 73be389bb914e..7b5cbf7d58f55 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -42,9 +42,8 @@ use option::Option::{Some, None}; use ops::{Deref, DerefMut, FnOnce}; use result::Result::{Ok, Err}; use rt; -use rustrt; -use rustrt::local::Local; -use rustrt::task::Task; +use rt::local::Local; +use rt::task::Task; use slice::SliceExt; use str::StrPrelude; use string::String; @@ -345,7 +344,7 @@ fn with_task_stdout(f: F) where }); result } else { - let mut io = rustrt::Stdout; + let mut io = rt::util::Stdout; f(&mut io as &mut Writer) }; match result { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e99aba9b67380..d7f331b6c230e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -104,7 +104,7 @@ html_playground_url = "http://play.rust-lang.org/")] #![allow(unknown_features)] -#![feature(macro_rules, globs, linkage)] +#![feature(macro_rules, globs, linkage, thread_local, asm)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] #![feature(import_shadowing, slicing_syntax)] #![feature(unboxed_closures)] @@ -124,7 +124,6 @@ extern crate core; extern crate "collections" as core_collections; extern crate "rand" as core_rand; extern crate libc; -extern crate rustrt; // Make std testable by not duplicating lang items. See #2912 #[cfg(test)] extern crate "std" as realstd; @@ -167,12 +166,8 @@ pub use core_collections::str; pub use core_collections::string; pub use core_collections::vec; -pub use rustrt::c_str; - pub use unicode::char; -pub use rustrt::thunk; - /* Exported macros */ pub mod macros; @@ -207,6 +202,7 @@ pub mod prelude; #[path = "num/f64.rs"] pub mod f64; pub mod ascii; +pub mod thunk; /* Common traits */ @@ -216,6 +212,7 @@ pub mod num; /* Runtime and platform support */ pub mod thread_local; +pub mod c_str; pub mod c_vec; pub mod dynamic_lib; pub mod fmt; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 860f9d2670a07..6e02c03602f1c 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1042,9 +1042,9 @@ fn real_args_as_bytes() -> Vec> { target_os = "freebsd", target_os = "dragonfly"))] fn real_args_as_bytes() -> Vec> { - use rustrt; + use rt; - match rustrt::args::clone() { + match rt::args::clone() { Some(args) => args, None => panic!("process arguments not initialized") } diff --git a/src/librustrt/args.rs b/src/libstd/rt/args.rs similarity index 95% rename from src/librustrt/args.rs rename to src/libstd/rt/args.rs index c1b48e989a1c1..8b9dbf73c53b9 100644 --- a/src/librustrt/args.rs +++ b/src/libstd/rt/args.rs @@ -20,7 +20,7 @@ //! FIXME #7756: Would be nice for this to not exist. use core::prelude::*; -use collections::vec::Vec; +use vec::Vec; /// One-time global initialization. pub unsafe fn init(argc: int, argv: *const *const u8) { imp::init(argc, argv) } @@ -46,12 +46,12 @@ pub fn clone() -> Option>> { imp::clone() } mod imp { use core::prelude::*; - use alloc::boxed::Box; - use collections::vec::Vec; - use collections::string::String; - use core::mem; + use boxed::Box; + use vec::Vec; + use string::String; + use mem; - use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; static mut GLOBAL_ARGS_PTR: uint = 0; static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; @@ -145,7 +145,7 @@ mod imp { target_os = "windows"))] mod imp { use core::prelude::*; - use collections::vec::Vec; + use vec::Vec; pub unsafe fn init(_argc: int, _argv: *const *const u8) { } diff --git a/src/librustrt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs similarity index 93% rename from src/librustrt/at_exit_imp.rs rename to src/libstd/rt/at_exit_imp.rs index 8be77d9b34ddd..086079c312ad4 100644 --- a/src/librustrt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -14,13 +14,13 @@ use core::prelude::*; -use alloc::boxed::Box; -use collections::vec::Vec; -use core::atomic; -use core::mem; -use thunk::{Thunk}; +use boxed::Box; +use vec::Vec; +use sync::atomic; +use mem; +use thunk::Thunk; -use exclusive::Exclusive; +use rt::exclusive::Exclusive; type Queue = Exclusive>; diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index d815a5ea4f702..40885823a0596 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -19,9 +19,8 @@ use os; use result::Result::{Ok, Err}; use str::{StrPrelude, from_str}; use sync::atomic; -use unicode::char::UnicodeChar; -pub use self::imp::write; +pub use sys::backtrace::write; // For now logging is turned off by default, and this function checks to see // whether the magical environment variable is present to see if it's turned on. @@ -41,984 +40,13 @@ pub fn log_enabled() -> bool { val == 2 } -#[cfg(target_word_size = "64")] const HEX_WIDTH: uint = 18; -#[cfg(target_word_size = "32")] const HEX_WIDTH: uint = 10; - -// All rust symbols are in theory lists of "::"-separated identifiers. Some -// assemblers, however, can't handle these characters in symbol names. To get -// around this, we use C++-style mangling. The mangling method is: -// -// 1. Prefix the symbol with "_ZN" -// 2. For each element of the path, emit the length plus the element -// 3. End the path with "E" -// -// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". -// -// We're the ones printing our backtraces, so we can't rely on anything else to -// demangle our symbols. It's *much* nicer to look at demangled symbols, so -// this function is implemented to give us nice pretty output. -// -// Note that this demangler isn't quite as fancy as it could be. We have lots -// of other information in our symbols like hashes, version, type information, -// etc. Additionally, this doesn't handle glue symbols at all. -fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { - // First validate the symbol. If it doesn't look like anything we're - // expecting, we just print it literally. Note that we must handle non-rust - // symbols because we could have any function in the backtrace. - let mut valid = true; - let mut inner = s; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { - inner = s.slice(3, s.len() - 1); - // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { - inner = s.slice(2, s.len() - 1); - } else { - valid = false; - } - - if valid { - let mut chars = inner.chars(); - while valid { - let mut i = 0; - for c in chars { - if c.is_numeric() { - i = i * 10 + c as uint - '0' as uint; - } else { - break - } - } - if i == 0 { - valid = chars.next().is_none(); - break - } else if chars.by_ref().take(i - 1).count() != i - 1 { - valid = false; - } - } - } - - // Alright, let's do this. - if !valid { - try!(writer.write_str(s)); - } else { - let mut first = true; - while inner.len() > 0 { - if !first { - try!(writer.write_str("::")); - } else { - first = false; - } - let mut rest = inner; - while rest.char_at(0).is_numeric() { - rest = rest.slice_from(1); - } - let i: uint = from_str(inner.slice_to(inner.len() - rest.len())).unwrap(); - inner = rest.slice_from(i); - rest = rest.slice_to(i); - while rest.len() > 0 { - if rest.starts_with("$") { - macro_rules! demangle { - ($($pat:expr => $demangled:expr),*) => ({ - $(if rest.starts_with($pat) { - try!(writer.write_str($demangled)); - rest = rest.slice_from($pat.len()); - } else)* - { - try!(writer.write_str(rest)); - break; - } - - }) - } - - // see src/librustc/back/link.rs for these mappings - demangle! ( - "$SP$" => "@", - "$UP$" => "Box", - "$RP$" => "*", - "$BP$" => "&", - "$LT$" => "<", - "$GT$" => ">", - "$LP$" => "(", - "$RP$" => ")", - "$C$" => ",", - - // in theory we can demangle any Unicode code point, but - // for simplicity we just catch the common ones. - "$x20" => " ", - "$x27" => "'", - "$x5b" => "[", - "$x5d" => "]" - ) - } else { - let idx = match rest.find('$') { - None => rest.len(), - Some(i) => i, - }; - try!(writer.write_str(rest.slice_to(idx))); - rest = rest.slice_from(idx); - } - } - } - } - - Ok(()) -} - -/// Backtrace support built on libgcc with some extra OS-specific support -/// -/// Some methods of getting a backtrace: -/// -/// * The backtrace() functions on unix. It turns out this doesn't work very -/// well for green threads on OSX, and the address to symbol portion of it -/// suffers problems that are described below. -/// -/// * Using libunwind. This is more difficult than it sounds because libunwind -/// isn't installed everywhere by default. It's also a bit of a hefty library, -/// so possibly not the best option. When testing, libunwind was excellent at -/// getting both accurate backtraces and accurate symbols across platforms. -/// This route was not chosen in favor of the next option, however. -/// -/// * We're already using libgcc_s for exceptions in rust (triggering task -/// unwinding and running destructors on the stack), and it turns out that it -/// conveniently comes with a function that also gives us a backtrace. All of -/// these functions look like _Unwind_*, but it's not quite the full -/// repertoire of the libunwind API. Due to it already being in use, this was -/// the chosen route of getting a backtrace. -/// -/// After choosing libgcc_s for backtraces, the sad part is that it will only -/// give us a stack trace of instruction pointers. Thankfully these instruction -/// pointers are accurate (they work for green and native threads), but it's -/// then up to us again to figure out how to translate these addresses to -/// symbols. As with before, we have a few options. Before, that, a little bit -/// of an interlude about symbols. This is my very limited knowledge about -/// symbol tables, and this information is likely slightly wrong, but the -/// general idea should be correct. -/// -/// When talking about symbols, it's helpful to know a few things about where -/// symbols are located. Some symbols are located in the dynamic symbol table -/// of the executable which in theory means that they're available for dynamic -/// linking and lookup. Other symbols end up only in the local symbol table of -/// the file. This loosely corresponds to pub and priv functions in Rust. -/// -/// Armed with this knowledge, we know that our solution for address to symbol -/// translation will need to consult both the local and dynamic symbol tables. -/// With that in mind, here's our options of translating an address to -/// a symbol. -/// -/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() -/// behind the scenes to translate, and this is why backtrace() was not used. -/// Conveniently, this method works fantastically on OSX. It appears dladdr() -/// uses magic to consult the local symbol table, or we're putting everything -/// in the dynamic symbol table anyway. Regardless, for OSX, this is the -/// method used for translation. It's provided by the system and easy to do.o -/// -/// Sadly, all other systems have a dladdr() implementation that does not -/// consult the local symbol table. This means that most functions are blank -/// because they don't have symbols. This means that we need another solution. -/// -/// * Use unw_get_proc_name(). This is part of the libunwind api (not the -/// libgcc_s version of the libunwind api), but involves taking a dependency -/// to libunwind. We may pursue this route in the future if we bundle -/// libunwind, but libunwind was unwieldy enough that it was not chosen at -/// this time to provide this functionality. -/// -/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a -/// semi-reasonable solution. The stdlib already knows how to spawn processes, -/// so in theory it could invoke readelf, parse the output, and consult the -/// local/dynamic symbol tables from there. This ended up not getting chosen -/// due to the craziness of the idea plus the advent of the next option. -/// -/// * Use `libbacktrace`. It turns out that this is a small library bundled in -/// the gcc repository which provides backtrace and symbol translation -/// functionality. All we really need from it is the backtrace functionality, -/// and we only really need this on everything that's not OSX, so this is the -/// chosen route for now. -/// -/// In summary, the current situation uses libgcc_s to get a trace of stack -/// pointers, and we use dladdr() or libbacktrace to translate these addresses -/// to symbols. This is a bit of a hokey implementation as-is, but it works for -/// all unix platforms we support right now, so it at least gets the job done. -#[cfg(unix)] -mod imp { - use prelude::*; - - use c_str::CString; - use io::IoResult; - use libc; - use mem; - use sync::{StaticMutex, MUTEX_INIT}; - - /// As always - iOS on arm uses SjLj exceptions and - /// _Unwind_Backtrace is even not available there. Still, - /// backtraces could be extracted using a backtrace function, - /// which thanks god is public - /// - /// As mentioned in a huge comment block above, backtrace doesn't - /// play well with green threads, so while it is extremely nice - /// and simple to use it should be used only on iOS devices as the - /// only viable option. - #[cfg(all(target_os = "ios", target_arch = "arm"))] - #[inline(never)] - pub fn write(w: &mut Writer) -> IoResult<()> { - use result; - - extern { - fn backtrace(buf: *mut *mut libc::c_void, - sz: libc::c_int) -> libc::c_int; - } - - // while it doesn't requires lock for work as everything is - // local, it still displays much nicer backtraces when a - // couple of tasks panic simultaneously - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - // 100 lines should be enough - const SIZE: uint = 100; - let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; - let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; - - // skipping the first one as it is write itself - let iter = range(1, cnt).map(|i| { - print(w, i as int, buf[i]) - }); - result::fold(iter, (), |_, _| ()) - } - - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - #[inline(never)] // if we know this is a function call, we can skip it when - // tracing - pub fn write(w: &mut Writer) -> IoResult<()> { - use io::IoError; - - struct Context<'a> { - idx: int, - writer: &'a mut (Writer+'a), - last_error: Option, - } - - // When using libbacktrace, we use some necessary global state, so we - // need to prevent more than one thread from entering this block. This - // is semi-reasonable in terms of printing anyway, and we know that all - // I/O done here is blocking I/O, not green I/O, so we don't have to - // worry about this being a native vs green mutex. - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - try!(writeln!(w, "stack backtrace:")); - - let mut cx = Context { writer: w, last_error: None, idx: 0 }; - return match unsafe { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context as *mut libc::c_void) - } { - uw::_URC_NO_REASON => { - match cx.last_error { - Some(err) => Err(err), - None => Ok(()) - } - } - _ => Ok(()), - }; - - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx: &mut Context = unsafe { mem::transmute(arg) }; - let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - // Don't print out the first few frames (they're not user frames) - cx.idx += 1; - if cx.idx <= 0 { return uw::_URC_NO_REASON } - // Don't print ginormous backtraces - if cx.idx > 100 { - match write!(cx.writer, " ... \n") { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - return uw::_URC_FAILURE - } - - // Once we hit an error, stop trying to print more frames - if cx.last_error.is_some() { return uw::_URC_FAILURE } - - match print(cx.writer, cx.idx, ip) { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - - // keep going - return uw::_URC_NO_REASON - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use intrinsics; - #[repr(C)] - struct Dl_info { - dli_fname: *const libc::c_char, - dli_fbase: *mut libc::c_void, - dli_sname: *const libc::c_char, - dli_saddr: *mut libc::c_void, - } - extern { - fn dladdr(addr: *const libc::c_void, - info: *mut Dl_info) -> libc::c_int; - } - - let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { - output(w, idx,addr, None) - } else { - output(w, idx, addr, Some(unsafe { - CString::new(info.dli_sname, false) - })) - } - } - - #[cfg(not(any(target_os = "macos", target_os = "ios")))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use os; - use ptr; - - //////////////////////////////////////////////////////////////////////// - // libbacktrace.h API - //////////////////////////////////////////////////////////////////////// - type backtrace_syminfo_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - symname: *const libc::c_char, - symval: libc::uintptr_t, - symsize: libc::uintptr_t); - type backtrace_error_callback = - extern "C" fn(data: *mut libc::c_void, - msg: *const libc::c_char, - errnum: libc::c_int); - enum backtrace_state {} - #[link(name = "backtrace", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - fn backtrace_create_state(filename: *const libc::c_char, - threaded: libc::c_int, - error: backtrace_error_callback, - data: *mut libc::c_void) - -> *mut backtrace_state; - fn backtrace_syminfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_syminfo_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - } - - //////////////////////////////////////////////////////////////////////// - // helper callbacks - //////////////////////////////////////////////////////////////////////// - - extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now - } - extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } - } - - // The libbacktrace API supports creating a state, but it does not - // support destroying a state. I personally take this to mean that a - // state is meant to be created and then live forever. - // - // I would love to register an at_exit() handler which cleans up this - // state, but libbacktrace provides no way to do so. - // - // With these constraints, this function has a statically cached state - // that is calculated the first time this is requested. Remember that - // backtracing all happens serially (one global lock). - // - // An additionally oddity in this function is that we initialize the - // filename via self_exe_name() to pass to libbacktrace. It turns out - // that on Linux libbacktrace seamlessly gets the filename of the - // current executable, but this fails on freebsd. by always providing - // it, we make sure that libbacktrace never has a reason to not look up - // the symbols. The libbacktrace API also states that the filename must - // be in "permanent memory", so we copy it to a static and then use the - // static as the pointer. - // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; - static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; - if !STATE.is_null() { return STATE } - let selfname = if cfg!(target_os = "freebsd") || - cfg!(target_os = "dragonfly") { - os::self_exe_name() - } else { - None - }; - let filename = match selfname { - Some(path) => { - let bytes = path.as_vec(); - if bytes.len() < LAST_FILENAME.len() { - let i = bytes.iter(); - for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { - *slot = *val as libc::c_char; - } - LAST_FILENAME.as_ptr() - } else { - ptr::null() - } - } - None => ptr::null(), - }; - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - return STATE - } - - //////////////////////////////////////////////////////////////////////// - // translation - //////////////////////////////////////////////////////////////////////// - - // backtrace errors are currently swept under the rug, only I/O - // errors are reported - let state = unsafe { init_state() }; - if state.is_null() { - return output(w, idx, addr, None) - } - let mut data = 0 as *const libc::c_char; - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_syminfo(state, addr as libc::uintptr_t, - syminfo_cb, error_cb, - data_addr as *mut libc::c_void) - }; - if ret == 0 || data.is_null() { - output(w, idx, addr, None) - } else { - output(w, idx, addr, Some(unsafe { CString::new(data, false) })) - } - } - - // Finally, after all that work above, we can emit a symbol. - fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, - s: Option) -> IoResult<()> { - try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH)); - match s.as_ref().and_then(|c| c.as_str()) { - Some(string) => try!(super::demangle(w, string)), - None => try!(write!(w, "")), - } - w.write(&['\n' as u8]) - } - - /// Unwind library interface used for backtraces - /// - /// Note that the native libraries come from librustrt, not this - /// module. - /// Note that dead code is allowed as here are just bindings - /// iOS doesn't use all of them it but adding more - /// platform-specific configs pollutes the code too much - #[allow(non_camel_case_types)] - #[allow(non_snake_case)] - #[allow(dead_code)] - mod uw { - pub use self::_Unwind_Reason_Code::*; - - 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: *mut _Unwind_Context, - arg: *mut libc::c_void) -> _Unwind_Reason_Code; - - extern { - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut 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(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIP(ctx: *mut _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: *mut _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, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, - _Unwind_VRS_DataRepresentation::_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(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void - { - pc - } - } -} - -/// As always, windows has something very different than unix, we mainly want -/// to avoid having to depend too much on libunwind for windows. -/// -/// If you google around, you'll find a fair bit of references to built-in -/// functions to get backtraces on windows. It turns out that most of these are -/// in an external library called dbghelp. I was unable to find this library -/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent -/// of it. -/// -/// You'll also find that there's a function called CaptureStackBackTrace -/// mentioned frequently (which is also easy to use), but sadly I didn't have a -/// copy of that function in my mingw install (maybe it was broken?). Instead, -/// this takes the route of using StackWalk64 in order to walk the stack. -#[cfg(windows)] -#[allow(dead_code, non_snake_case)] -mod imp { - use c_str::CString; - use intrinsics; - use io::{IoResult, Writer}; - use libc; - use mem; - use ops::Drop; - use option::Option::{Some, None}; - use path::Path; - use result::Result::{Ok, Err}; - use sync::{StaticMutex, MUTEX_INIT}; - use slice::SliceExt; - use str::StrPrelude; - use dynamic_lib::DynamicLibrary; - - #[allow(non_snake_case)] - extern "system" { - fn GetCurrentProcess() -> libc::HANDLE; - fn GetCurrentThread() -> libc::HANDLE; - fn RtlCaptureContext(ctx: *mut arch::CONTEXT); - } - - type SymFromAddrFn = - extern "system" fn(libc::HANDLE, u64, *mut u64, - *mut SYMBOL_INFO) -> libc::BOOL; - type SymInitializeFn = - extern "system" fn(libc::HANDLE, *mut libc::c_void, - libc::BOOL) -> libc::BOOL; - type SymCleanupFn = - extern "system" fn(libc::HANDLE) -> libc::BOOL; - - type StackWalk64Fn = - extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, - *mut STACKFRAME64, *mut arch::CONTEXT, - *mut libc::c_void, *mut libc::c_void, - *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; - - const MAX_SYM_NAME: uint = 2000; - const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; - const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; - const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; - - #[repr(C)] - struct SYMBOL_INFO { - SizeOfStruct: libc::c_ulong, - TypeIndex: libc::c_ulong, - Reserved: [u64, ..2], - Index: libc::c_ulong, - Size: libc::c_ulong, - ModBase: u64, - Flags: libc::c_ulong, - Value: u64, - Address: u64, - Register: libc::c_ulong, - Scope: libc::c_ulong, - Tag: libc::c_ulong, - NameLen: libc::c_ulong, - MaxNameLen: libc::c_ulong, - // note that windows has this as 1, but it basically just means that - // the name is inline at the end of the struct. For us, we just bump - // the struct size up to MAX_SYM_NAME. - Name: [libc::c_char, ..MAX_SYM_NAME], - } - - - #[repr(C)] - enum ADDRESS_MODE { - AddrMode1616, - AddrMode1632, - AddrModeReal, - AddrModeFlat, - } - - struct ADDRESS64 { - Offset: u64, - Segment: u16, - Mode: ADDRESS_MODE, - } - - struct STACKFRAME64 { - AddrPC: ADDRESS64, - AddrReturn: ADDRESS64, - AddrFrame: ADDRESS64, - AddrStack: ADDRESS64, - AddrBStore: ADDRESS64, - FuncTableEntry: *mut libc::c_void, - Params: [u64, ..4], - Far: libc::BOOL, - Virtual: libc::BOOL, - Reserved: [u64, ..3], - KdHelp: KDHELP64, - } - - struct KDHELP64 { - Thread: u64, - ThCallbackStack: libc::DWORD, - ThCallbackBStore: libc::DWORD, - NextCallback: libc::DWORD, - FramePointer: libc::DWORD, - KiCallUserMode: u64, - KeUserCallbackDispatcher: u64, - SystemRangeStart: u64, - KiUserExceptionDispatcher: u64, - StackBase: u64, - StackLimit: u64, - Reserved: [u64, ..5], - } - - #[cfg(target_arch = "x86")] - mod arch { - use libc; - - const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; - - #[repr(C)] - pub struct CONTEXT { - ContextFlags: libc::DWORD, - Dr0: libc::DWORD, - Dr1: libc::DWORD, - Dr2: libc::DWORD, - Dr3: libc::DWORD, - Dr6: libc::DWORD, - Dr7: libc::DWORD, - FloatSave: FLOATING_SAVE_AREA, - SegGs: libc::DWORD, - SegFs: libc::DWORD, - SegEs: libc::DWORD, - SegDs: libc::DWORD, - Edi: libc::DWORD, - Esi: libc::DWORD, - Ebx: libc::DWORD, - Edx: libc::DWORD, - Ecx: libc::DWORD, - Eax: libc::DWORD, - Ebp: libc::DWORD, - Eip: libc::DWORD, - SegCs: libc::DWORD, - EFlags: libc::DWORD, - Esp: libc::DWORD, - SegSs: libc::DWORD, - ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - ControlWord: libc::DWORD, - StatusWord: libc::DWORD, - TagWord: libc::DWORD, - ErrorOffset: libc::DWORD, - ErrorSelector: libc::DWORD, - DataOffset: libc::DWORD, - DataSelector: libc::DWORD, - RegisterArea: [u8, ..80], - Cr0NpxState: libc::DWORD, - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> libc::DWORD { - frame.AddrPC.Offset = ctx.Eip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Esp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Ebp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_I386 - } - } - - #[cfg(target_arch = "x86_64")] - mod arch { - use libc::{c_longlong, c_ulonglong}; - use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; - use simd; - - #[repr(C)] - pub struct CONTEXT { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - P1Home: DWORDLONG, - P2Home: DWORDLONG, - P3Home: DWORDLONG, - P4Home: DWORDLONG, - P5Home: DWORDLONG, - P6Home: DWORDLONG, - - ContextFlags: DWORD, - MxCsr: DWORD, - - SegCs: WORD, - SegDs: WORD, - SegEs: WORD, - SegFs: WORD, - SegGs: WORD, - SegSs: WORD, - EFlags: DWORD, - - Dr0: DWORDLONG, - Dr1: DWORDLONG, - Dr2: DWORDLONG, - Dr3: DWORDLONG, - Dr6: DWORDLONG, - Dr7: DWORDLONG, - - Rax: DWORDLONG, - Rcx: DWORDLONG, - Rdx: DWORDLONG, - Rbx: DWORDLONG, - Rsp: DWORDLONG, - Rbp: DWORDLONG, - Rsi: DWORDLONG, - Rdi: DWORDLONG, - R8: DWORDLONG, - R9: DWORDLONG, - R10: DWORDLONG, - R11: DWORDLONG, - R12: DWORDLONG, - R13: DWORDLONG, - R14: DWORDLONG, - R15: DWORDLONG, - - Rip: DWORDLONG, - - FltSave: FLOATING_SAVE_AREA, - - VectorRegister: [M128A, .. 26], - VectorControl: DWORDLONG, - - DebugControl: DWORDLONG, - LastBranchToRip: DWORDLONG, - LastBranchFromRip: DWORDLONG, - LastExceptionToRip: DWORDLONG, - LastExceptionFromRip: DWORDLONG, - } - - #[repr(C)] - pub struct M128A { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - Low: c_ulonglong, - High: c_longlong - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - _Dummy: [u8, ..512] // FIXME: Fill this out - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> DWORD { - frame.AddrPC.Offset = ctx.Rip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Rsp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Rbp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_AMD64 - } - } - - #[repr(C)] - struct Cleanup { - handle: libc::HANDLE, - SymCleanup: SymCleanupFn, - } - - impl Drop for Cleanup { - fn drop(&mut self) { (self.SymCleanup)(self.handle); } - } - - pub fn write(w: &mut Writer) -> IoResult<()> { - // According to windows documentation, all dbghelp functions are - // single-threaded. - static LOCK: StaticMutex = MUTEX_INIT; - let _g = LOCK.lock(); - - // Open up dbghelp.dll, we don't link to it explicitly because it can't - // always be found. Additionally, it's nice having fewer dependencies. - let path = Path::new("dbghelp.dll"); - let lib = match DynamicLibrary::open(Some(&path)) { - Ok(lib) => lib, - Err(..) => return Ok(()), - }; - - macro_rules! sym { ($e:expr, $t:ident) => (unsafe { - match lib.symbol($e) { - Ok(f) => mem::transmute::<*mut u8, $t>(f), - Err(..) => return Ok(()) - } - }) } - - // Fetch the symbols necessary from dbghelp.dll - let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); - let SymInitialize = sym!("SymInitialize", SymInitializeFn); - let SymCleanup = sym!("SymCleanup", SymCleanupFn); - let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); - - // Allocate necessary structures for doing the stack walk - let process = unsafe { GetCurrentProcess() }; - let thread = unsafe { GetCurrentThread() }; - let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; - unsafe { RtlCaptureContext(&mut context); } - let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; - let image = arch::init_frame(&mut frame, &context); - - // Initialize this process's symbols - let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); - if ret != libc::TRUE { return Ok(()) } - let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; - - // And now that we're done with all the setup, do the stack walking! - let mut i = 0i; - try!(write!(w, "stack backtrace:\n")); - while StackWalk64(image, process, thread, &mut frame, &mut context, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void) == libc::TRUE{ - let addr = frame.AddrPC.Offset; - if addr == frame.AddrReturn.Offset || addr == 0 || - frame.AddrReturn.Offset == 0 { break } - - i += 1; - try!(write!(w, " {:2}: {:#2$x}", i, addr, super::HEX_WIDTH)); - let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; - info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; - // the struct size in C. the value is different to - // `size_of::() - MAX_SYM_NAME + 1` (== 81) - // due to struct alignment. - info.SizeOfStruct = 88; - - let mut displacement = 0u64; - let ret = SymFromAddr(process, addr as u64, &mut displacement, - &mut info); - - if ret == libc::TRUE { - try!(write!(w, " - ")); - let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; - let bytes = cstr.as_bytes(); - match cstr.as_str() { - Some(s) => try!(super::demangle(w, s)), - None => try!(w.write(bytes[..bytes.len()-1])), - } - if displacement != 0 { - try!(write!(w, "+{:#x}", displacement)); - } - } - try!(w.write(&['\n' as u8])); - } - - Ok(()) - } -} - #[cfg(test)] mod test { use prelude::*; - use io::MemWriter; - + use sys_common; macro_rules! t { ($a:expr, $b:expr) => ({ let mut m = Vec::new(); - super::demangle(&mut m, $a).unwrap(); + sys_common::backtrace::demangle(&mut m, $a).unwrap(); assert_eq!(String::from_utf8(m).unwrap(), $b); }) } diff --git a/src/librustrt/bookkeeping.rs b/src/libstd/rt/bookkeeping.rs similarity index 95% rename from src/librustrt/bookkeeping.rs rename to src/libstd/rt/bookkeeping.rs index e918a496d5531..aca520fc088f5 100644 --- a/src/librustrt/bookkeeping.rs +++ b/src/libstd/rt/bookkeeping.rs @@ -18,10 +18,10 @@ //! each respective runtime to make sure that they call increment() and //! decrement() manually. -use core::atomic; -use core::ops::Drop; +use sync::atomic; +use ops::Drop; -use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; static TASK_COUNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; static TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; diff --git a/src/librustrt/exclusive.rs b/src/libstd/rt/exclusive.rs similarity index 96% rename from src/librustrt/exclusive.rs rename to src/libstd/rt/exclusive.rs index 1d8ea2202bf0d..1d3082d1b4cc4 100644 --- a/src/librustrt/exclusive.rs +++ b/src/libstd/rt/exclusive.rs @@ -10,8 +10,8 @@ use core::prelude::*; -use core::cell::UnsafeCell; -use mutex; +use cell::UnsafeCell; +use rt::mutex; /// An OS mutex over some data. /// @@ -79,10 +79,10 @@ impl<'a, T: Send> DerefMut for ExclusiveGuard<'a, T> { #[cfg(test)] mod tests { - use std::prelude::*; - use alloc::arc::Arc; + use prelude::*; + use sync::Arc; use super::Exclusive; - use std::task; + use task; #[test] fn exclusive_new_arc() { diff --git a/src/librustrt/libunwind.rs b/src/libstd/rt/libunwind.rs similarity index 100% rename from src/librustrt/libunwind.rs rename to src/libstd/rt/libunwind.rs diff --git a/src/librustrt/local.rs b/src/libstd/rt/local.rs similarity index 96% rename from src/librustrt/local.rs rename to src/libstd/rt/local.rs index df733d7ee225b..089960a6bc80b 100644 --- a/src/librustrt/local.rs +++ b/src/libstd/rt/local.rs @@ -10,9 +10,9 @@ use core::prelude::*; -use alloc::boxed::Box; -use local_ptr; -use task::Task; +use boxed::Box; +use rt::local_ptr; +use rt::task::Task; /// Encapsulates some task-local data. pub trait Local { @@ -52,10 +52,10 @@ impl Local> for Task { #[cfg(test)] mod test { - use std::prelude::*; - use thread::Thread; + use prelude::*; use super::*; - use task::Task; + use super::super::thread::Thread; + use super::super::task::Task; #[test] fn thread_local_task_smoke_test() { diff --git a/src/librustrt/local_ptr.rs b/src/libstd/rt/local_ptr.rs similarity index 97% rename from src/librustrt/local_ptr.rs rename to src/libstd/rt/local_ptr.rs index 58f8f8b310f55..a87bc3d27662e 100644 --- a/src/librustrt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -19,8 +19,8 @@ use core::prelude::*; -use core::mem; -use alloc::boxed::Box; +use mem; +use boxed::Box; #[cfg(any(windows, // mingw-w32 doesn't like thread_local things target_os = "android", // see #10686 @@ -86,11 +86,11 @@ pub unsafe fn borrow() -> Borrowed { pub mod compiled { use core::prelude::*; - use alloc::boxed::Box; - use core::mem; + use boxed::Box; + use mem; #[cfg(test)] - pub use realrustrt::shouldnt_be_public::RT_TLS_PTR; + pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; #[cfg(not(test))] #[thread_local] @@ -237,10 +237,10 @@ pub mod compiled { pub mod native { use core::prelude::*; - use alloc::boxed::Box; - use core::mem; - use core::ptr; - use thread_local_storage as tls; + use boxed::Box; + use mem; + use ptr; + use rt::thread_local_storage as tls; 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 realrustrt; + use rt; unsafe { - mem::transmute(realrustrt::shouldnt_be_public::maybe_tls_key()) + mem::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) } } } diff --git a/src/librustrt/macros.rs b/src/libstd/rt/macros.rs similarity index 86% rename from src/librustrt/macros.rs rename to src/libstd/rt/macros.rs index 4514430853572..bee8b5b82f48e 100644 --- a/src/librustrt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -17,7 +17,7 @@ macro_rules! rterrln { ($fmt:expr $($arg:tt)*) => ( { - format_args!(::util::dumb_print, concat!($fmt, "\n") $($arg)*) + format_args!(::rt::util::dumb_print, concat!($fmt, "\n") $($arg)*) } ) } @@ -32,7 +32,7 @@ macro_rules! rtdebug { macro_rules! rtassert { ( $arg:expr ) => ( { - if ::util::ENFORCE_SANITY { + if ::rt::util::ENFORCE_SANITY { if !$arg { rtabort!(" assertion failed: {}", stringify!($arg)); } @@ -40,7 +40,6 @@ macro_rules! rtassert { } ) } - macro_rules! rtabort { - ($($arg:tt)*) => (format_args!(::util::abort, $($arg)*)) + ($($arg:tt)*) => (format_args!(::rt::util::abort, $($arg)*)) } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index eb517047ddc0b..21c8197ef0558 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -50,23 +50,43 @@ use borrow::IntoCow; use failure; -use rustrt; use os; use thunk::Thunk; +use kinds::Send; +use sys_common; // Reexport some of our utilities which are expected by other crates. pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; +pub use self::unwind::{begin_unwind, begin_unwind_fmt}; -// Reexport functionality from librustrt and other crates underneath the -// standard library which work together to create the entire runtime. +// Reexport some functionality from liballoc. pub use alloc::heap; -pub use rustrt::{begin_unwind, begin_unwind_fmt, at_exit}; // Simple backtrace functionality (to print on panic) pub mod backtrace; -// Just stuff -mod util; +// Internals +mod macros; + +// These should be refactored/moved/made private over time +pub mod mutex; +pub mod thread; +pub mod exclusive; +pub mod util; +pub mod bookkeeping; +pub mod local; +pub mod task; +pub mod unwind; + +mod args; +mod at_exit_imp; +mod libunwind; +mod local_ptr; +mod thread_local_storage; + +/// The default error code of the rust runtime if the main task panics instead +/// of exiting cleanly. +pub const DEFAULT_ERROR_CODE: int = 101; /// One-time runtime initialization. /// @@ -75,8 +95,15 @@ mod util; /// metadata, and storing the process arguments. #[allow(experimental)] pub fn init(argc: int, argv: *const *const u8) { - rustrt::init(argc, argv); - unsafe { rustrt::unwind::register(failure::on_fail); } + // 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(); + thread::init(); + unwind::register(failure::on_fail); + } } #[cfg(any(windows, android))] @@ -106,7 +133,8 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { use prelude::*; use rt; - use rustrt::task::Task; + use rt::task::Task; + use str; let something_around_the_top_of_the_stack = 1; let addr = &something_around_the_top_of_the_stack as *const int; @@ -139,18 +167,35 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { let mut exit_code = None; let mut main = Some(main); let mut task = box Task::new(Some((my_stack_bottom, my_stack_top)), - Some(rustrt::thread::main_guard_page())); - task.name = Some("
".into_cow()); + Some(rt::thread::main_guard_page())); + task.name = Some(str::Slice("
")); drop(task.run(|| { unsafe { - rustrt::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); + sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); } (main.take().unwrap()).invoke(()); exit_code = Some(os::get_exit_status()); }).destroy()); - unsafe { rt::cleanup(); } + unsafe { cleanup(); } // If the exit code wasn't set, then the task block must have panicked. - return exit_code.unwrap_or(rustrt::DEFAULT_ERROR_CODE); + return exit_code.unwrap_or(rt::DEFAULT_ERROR_CODE); +} + +/// 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. @@ -163,5 +208,18 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { /// Invoking cleanup while portions of the runtime are still in use may cause /// undefined behavior. pub unsafe fn cleanup() { - rustrt::cleanup(); + bookkeeping::wait_for_other_tasks(); + args::cleanup(); + thread::cleanup(); + local_ptr::cleanup(); + at_exit_imp::run(); +} + +// 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(all(not(windows), not(target_os = "android"), not(target_os = "ios")))] + pub use super::local_ptr::compiled::RT_TLS_PTR; } diff --git a/src/librustrt/mutex.rs b/src/libstd/rt/mutex.rs similarity index 52% rename from src/librustrt/mutex.rs rename to src/libstd/rt/mutex.rs index cbfb86a6ac7b2..381f14570dfbc 100644 --- a/src/librustrt/mutex.rs +++ b/src/libstd/rt/mutex.rs @@ -33,7 +33,7 @@ //! # Example //! //! ```rust -//! use rustrt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; +//! use rt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; //! //! // Use a statically initialized mutex //! static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; @@ -60,6 +60,8 @@ use core::prelude::*; +use sys::mutex as imp; + /// A native mutex suitable for storing in statics (that is, it has /// the `destroy` method rather than a destructor). /// @@ -108,7 +110,7 @@ impl StaticNativeMutex { /// # Example /// /// ```rust - /// use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + /// use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; /// static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; /// unsafe { /// let _guard = LOCK.lock(); @@ -225,7 +227,7 @@ impl NativeMutex { /// # Example /// /// ```rust - /// use rustrt::mutex::NativeMutex; + /// use rt::mutex::NativeMutex; /// unsafe { /// let mut lock = NativeMutex::new(); /// @@ -336,336 +338,13 @@ impl<'a> Drop for LockGuard<'a> { } } -#[cfg(unix)] -mod imp { - use libc; - use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, - pthread_mutex_t, pthread_cond_t}; - use core::cell::UnsafeCell; - - type pthread_mutexattr_t = libc::c_void; - type pthread_condattr_t = libc::c_void; - - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - mod os { - use libc; - - pub type pthread_mutex_t = *mut libc::c_void; - pub type pthread_cond_t = *mut libc::c_void; - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = - 0 as pthread_mutex_t; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = - 0 as pthread_cond_t; - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - mod os { - use core::kinds::Copy; - use libc; - - #[cfg(target_arch = "x86_64")] - const __PTHREAD_MUTEX_SIZE__: uint = 56; - #[cfg(target_arch = "x86_64")] - const __PTHREAD_COND_SIZE__: uint = 40; - #[cfg(target_arch = "x86")] - const __PTHREAD_MUTEX_SIZE__: uint = 40; - #[cfg(target_arch = "x86")] - const __PTHREAD_COND_SIZE__: uint = 24; - #[cfg(target_arch = "arm")] - const __PTHREAD_MUTEX_SIZE__: uint = 40; - #[cfg(target_arch = "arm")] - const __PTHREAD_COND_SIZE__: uint = 24; - - const _PTHREAD_MUTEX_SIG_INIT: libc::c_long = 0x32AAABA7; - const _PTHREAD_COND_SIG_INIT: libc::c_long = 0x3CB0B1BB; - - #[repr(C)] - pub struct pthread_mutex_t { - __sig: libc::c_long, - __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__], - } - - impl Copy for pthread_mutex_t {} - - #[repr(C)] - pub struct pthread_cond_t { - __sig: libc::c_long, - __opaque: [u8, ..__PTHREAD_COND_SIZE__], - } - - impl Copy for pthread_cond_t {} - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - __sig: _PTHREAD_MUTEX_SIG_INIT, - __opaque: [0, ..__PTHREAD_MUTEX_SIZE__], - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - __sig: _PTHREAD_COND_SIG_INIT, - __opaque: [0, ..__PTHREAD_COND_SIZE__], - }; - } - - #[cfg(target_os = "linux")] - mod os { - use core::kinds::Copy; - use libc; - - // minus 8 because we have an 'align' field - #[cfg(target_arch = "x86_64")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8; - #[cfg(target_arch = "x86")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "arm")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "mips")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "mipsel")] - const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; - #[cfg(target_arch = "x86_64")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "x86")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "arm")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "mips")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - #[cfg(target_arch = "mipsel")] - const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; - - #[repr(C)] - pub struct pthread_mutex_t { - __align: libc::c_longlong, - size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T], - } - - impl Copy for pthread_mutex_t {} - - #[repr(C)] - pub struct pthread_cond_t { - __align: libc::c_longlong, - size: [u8, ..__SIZEOF_PTHREAD_COND_T], - } - - impl Copy for pthread_cond_t {} - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - __align: 0, - size: [0, ..__SIZEOF_PTHREAD_MUTEX_T], - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - __align: 0, - size: [0, ..__SIZEOF_PTHREAD_COND_T], - }; - } - #[cfg(target_os = "android")] - mod os { - use libc; - - #[repr(C)] - pub struct pthread_mutex_t { value: libc::c_int } - #[repr(C)] - pub struct pthread_cond_t { value: libc::c_int } - - pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { - value: 0, - }; - pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { - value: 0, - }; - } - - pub struct Mutex { - lock: UnsafeCell, - cond: UnsafeCell, - } - - pub const MUTEX_INIT: Mutex = Mutex { - lock: UnsafeCell { value: PTHREAD_MUTEX_INITIALIZER }, - cond: UnsafeCell { value: PTHREAD_COND_INITIALIZER }, - }; - - impl Mutex { - pub unsafe fn new() -> Mutex { - // As mutex might be moved and address is changing it - // is better to avoid initialization of potentially - // opaque OS data before it landed - let m = Mutex { - lock: UnsafeCell::new(PTHREAD_MUTEX_INITIALIZER), - cond: UnsafeCell::new(PTHREAD_COND_INITIALIZER), - }; - - return m; - } - - pub unsafe fn lock(&self) { pthread_mutex_lock(self.lock.get()); } - pub unsafe fn unlock(&self) { pthread_mutex_unlock(self.lock.get()); } - pub unsafe fn signal(&self) { pthread_cond_signal(self.cond.get()); } - pub unsafe fn wait(&self) { - pthread_cond_wait(self.cond.get(), self.lock.get()); - } - pub unsafe fn trylock(&self) -> bool { - pthread_mutex_trylock(self.lock.get()) == 0 - } - pub unsafe fn destroy(&self) { - pthread_mutex_destroy(self.lock.get()); - pthread_cond_destroy(self.cond.get()); - } - } - - extern { - fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int; - fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int; - - fn pthread_cond_wait(cond: *mut pthread_cond_t, - lock: *mut pthread_mutex_t) -> libc::c_int; - fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int; - } -} - -#[cfg(windows)] -mod imp { - use alloc::heap; - use core::atomic; - use core::ptr; - use core::ptr::RawPtr; - use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR}; - use libc; - - type LPCRITICAL_SECTION = *mut c_void; - const SPIN_COUNT: DWORD = 4000; - #[cfg(target_arch = "x86")] - const CRIT_SECTION_SIZE: uint = 24; - #[cfg(target_arch = "x86_64")] - const CRIT_SECTION_SIZE: uint = 40; - - pub struct Mutex { - // pointers for the lock/cond handles, atomically updated - lock: atomic::AtomicUint, - cond: atomic::AtomicUint, - } - - pub const MUTEX_INIT: Mutex = Mutex { - lock: atomic::INIT_ATOMIC_UINT, - cond: atomic::INIT_ATOMIC_UINT, - }; - - impl Mutex { - pub unsafe fn new() -> Mutex { - Mutex { - lock: atomic::AtomicUint::new(init_lock()), - cond: atomic::AtomicUint::new(init_cond()), - } - } - pub unsafe fn lock(&self) { - EnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) - } - pub unsafe fn trylock(&self) -> bool { - TryEnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) != 0 - } - pub unsafe fn unlock(&self) { - LeaveCriticalSection(self.getlock() as LPCRITICAL_SECTION) - } - - pub unsafe fn wait(&self) { - self.unlock(); - WaitForSingleObject(self.getcond() as HANDLE, libc::INFINITE); - self.lock(); - } - - pub unsafe fn signal(&self) { - assert!(SetEvent(self.getcond() as HANDLE) != 0); - } - - /// This function is especially unsafe because there are no guarantees made - /// that no other thread is currently holding the lock or waiting on the - /// condition variable contained inside. - pub unsafe fn destroy(&self) { - let lock = self.lock.swap(0, atomic::SeqCst); - let cond = self.cond.swap(0, atomic::SeqCst); - if lock != 0 { free_lock(lock) } - if cond != 0 { free_cond(cond) } - } - - unsafe fn getlock(&self) -> *mut c_void { - match self.lock.load(atomic::SeqCst) { - 0 => {} - n => return n as *mut c_void - } - let lock = init_lock(); - match self.lock.compare_and_swap(0, lock, atomic::SeqCst) { - 0 => return lock as *mut c_void, - _ => {} - } - free_lock(lock); - return self.lock.load(atomic::SeqCst) as *mut c_void; - } - - unsafe fn getcond(&self) -> *mut c_void { - match self.cond.load(atomic::SeqCst) { - 0 => {} - n => return n as *mut c_void - } - let cond = init_cond(); - match self.cond.compare_and_swap(0, cond, atomic::SeqCst) { - 0 => return cond as *mut c_void, - _ => {} - } - free_cond(cond); - return self.cond.load(atomic::SeqCst) as *mut c_void; - } - } - - pub unsafe fn init_lock() -> uint { - let block = heap::allocate(CRIT_SECTION_SIZE, 8) as *mut c_void; - if block.is_null() { ::alloc::oom() } - InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT); - return block as uint; - } - - pub unsafe fn init_cond() -> uint { - return CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE, - ptr::null()) as uint; - } - - pub unsafe fn free_lock(h: uint) { - DeleteCriticalSection(h as LPCRITICAL_SECTION); - heap::deallocate(h as *mut u8, CRIT_SECTION_SIZE, 8); - } - - pub unsafe fn free_cond(h: uint) { - let block = h as HANDLE; - libc::CloseHandle(block); - } - - #[allow(non_snake_case)] - extern "system" { - fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES, - bManualReset: BOOL, - bInitialState: BOOL, - lpName: LPCSTR) -> HANDLE; - fn InitializeCriticalSectionAndSpinCount( - lpCriticalSection: LPCRITICAL_SECTION, - dwSpinCount: DWORD) -> BOOL; - fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); - fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; - fn SetEvent(hEvent: HANDLE) -> BOOL; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; - } -} - #[cfg(test)] mod test { - use std::prelude::*; + use prelude::*; - use std::mem::drop; + use mem::drop; use super::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - use thread::Thread; + use rt::thread::Thread; #[test] fn smoke_lock() { diff --git a/src/librustrt/task.rs b/src/libstd/rt/task.rs similarity index 96% rename from src/librustrt/task.rs rename to src/libstd/rt/task.rs index b942a3819cc41..babd111b3c221 100644 --- a/src/librustrt/task.rs +++ b/src/libstd/rt/task.rs @@ -15,27 +15,28 @@ pub use self::BlockedTask::*; use self::TaskState::*; -use alloc::arc::Arc; -use alloc::boxed::Box; -use core::any::Any; -use core::atomic::{AtomicUint, SeqCst}; -use core::iter::{IteratorExt, Take}; -use core::ops::FnOnce; -use core::mem; -use core::ops::FnMut; +use any::Any; +use boxed::Box; +use sync::Arc; +use sync::atomic::{AtomicUint, SeqCst}; +use iter::{IteratorExt, Take}; +use kinds::marker; +use mem; +use ops::FnMut; use core::prelude::{Clone, Drop, Err, Iterator, None, Ok, Option, Send, Some}; use core::prelude::{drop}; - -use bookkeeping; -use mutex::NativeMutex; -use local::Local; -use thread::{mod, Thread}; -use stack; -use unwind; -use unwind::Unwinder; -use collections::str::SendStr; +use str::SendStr; use thunk::Thunk; +use rt; +use rt::bookkeeping; +use rt::mutex::NativeMutex; +use rt::local::Local; +use rt::thread::{mod, Thread}; +use sys_common::stack; +use rt::unwind; +use rt::unwind::Unwinder; + /// State associated with Rust tasks. /// /// This structure is currently undergoing major changes, and is @@ -129,9 +130,7 @@ impl Task { task.name = name; task.death.on_exit = on_exit; - // FIXME: change this back after moving rustrt into std - // let stack = stack_size.unwrap_or(rt::min_stack()); - let stack = stack_size.unwrap_or(2 * 1024 * 1024); + let stack = stack_size.unwrap_or(rt::min_stack()); // Note that this increment must happen *before* the spawn in order to // guarantee that if this task exits it will always end up waiting for @@ -504,8 +503,9 @@ impl Death { #[cfg(test)] mod test { use super::*; - use std::prelude::*; - use std::task; + use prelude::*; + use task; + use rt::unwind; #[test] fn unwind() { @@ -519,7 +519,7 @@ mod test { #[test] fn rng() { - use std::rand::{StdRng, Rng}; + use rand::{StdRng, Rng}; let mut r = StdRng::new().ok().unwrap(); let _ = r.next_u32(); } @@ -541,7 +541,7 @@ mod test { #[test] #[should_fail] fn test_begin_unwind() { - use unwind::begin_unwind; + use rt::unwind::begin_unwind; begin_unwind("cause", &(file!(), line!())) } diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs new file mode 100644 index 0000000000000..c10338b1bceb2 --- /dev/null +++ b/src/libstd/rt/thread.rs @@ -0,0 +1,171 @@ +// Copyright 2013-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. + +//! Native os-thread management +//! +//! This modules contains bindings necessary for managing OS-level threads. +//! These functions operate outside of the rust runtime, creating threads +//! which are not used for scheduling in any way. + +#![allow(non_camel_case_types)] + +use core::prelude::*; + +use boxed::Box; +use mem; +use sys::stack_overflow; +use sys::thread as imp; + +pub unsafe fn init() { + imp::guard::init(); + stack_overflow::init(); +} + +pub unsafe fn cleanup() { + stack_overflow::cleanup(); +} + +/// This struct represents a native thread's state. This is used to join on an +/// existing thread created in the join-able state. +pub struct Thread { + native: imp::rust_thread, + joined: bool, + packet: Box>, +} + +static DEFAULT_STACK_SIZE: uint = 1024 * 1024; + +/// Returns the last writable byte of the main thread's stack next to the guard +/// page. Must be called from the main thread. +pub fn main_guard_page() -> uint { + unsafe { + imp::guard::main() + } +} + +/// Returns the last writable byte of the current thread's stack next to the +/// guard page. Must not be called from the main thread. +pub fn current_guard_page() -> uint { + unsafe { + imp::guard::current() + } +} + +// There are two impl blocks b/c if T were specified at the top then it's just a +// pain to specify a type parameter on Thread::spawn (which doesn't need the +// type parameter). +impl Thread<()> { + + /// Starts execution of a new OS thread. + /// + /// This function will not wait for the thread to join, but a handle to the + /// thread will be returned. + /// + /// Note that the handle returned is used to acquire the return value of the + /// procedure `main`. The `join` function will wait for the thread to finish + /// and return the value that `main` generated. + /// + /// Also note that the `Thread` returned will *always* wait for the thread + /// to finish executing. This means that even if `join` is not explicitly + /// called, when the `Thread` falls out of scope its destructor will block + /// waiting for the OS thread. + pub fn start(main: proc():Send -> T) -> Thread { + Thread::start_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `start`, but specifies an explicit + /// stack size for the new thread. + pub fn start_stack(stack: uint, main: proc():Send -> T) -> Thread { + + // We need the address of the packet to fill in to be stable so when + // `main` fills it in it's still valid, so allocate an extra box to do + // so. + let packet = box None; + let packet2: *mut Option = unsafe { + *mem::transmute::<&Box>, *const *mut Option>(&packet) + }; + let main = proc() unsafe { *packet2 = Some(main()); }; + let native = unsafe { imp::create(stack, box main) }; + + Thread { + native: native, + joined: false, + packet: packet, + } + } + + /// This will spawn a new thread, but it will not wait for the thread to + /// finish, nor is it possible to wait for the thread to finish. + /// + /// This corresponds to creating threads in the 'detached' state on unix + /// systems. Note that platforms may not keep the main program alive even if + /// there are detached thread still running around. + pub fn spawn(main: proc():Send) { + Thread::spawn_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `spawn`, but explicitly specifies a + /// stack size for the new thread. + pub fn spawn_stack(stack: uint, main: proc():Send) { + unsafe { + let handle = imp::create(stack, box main); + imp::detach(handle); + } + } + + /// Relinquishes the CPU slot that this OS-thread is currently using, + /// allowing another thread to run for awhile. + pub fn yield_now() { + unsafe { imp::yield_now(); } + } +} + +impl Thread { + /// Wait for this thread to finish, returning the result of the thread's + /// calculation. + pub fn join(mut self) -> T { + assert!(!self.joined); + unsafe { imp::join(self.native) }; + self.joined = true; + assert!(self.packet.is_some()); + self.packet.take().unwrap() + } +} + +#[unsafe_destructor] +impl Drop for Thread { + fn drop(&mut self) { + // This is required for correctness. If this is not done then the thread + // would fill in a return box which no longer exists. + if !self.joined { + unsafe { imp::join(self.native) }; + } + } +} + +#[cfg(test)] +mod tests { + use super::Thread; + + #[test] + fn smoke() { Thread::start(proc (){}).join(); } + + #[test] + fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } + + #[test] + fn detached() { Thread::spawn(proc () {}) } + + #[test] + fn small_stacks() { + assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); + assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); + } +} diff --git a/src/librustrt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs similarity index 96% rename from src/librustrt/thread_local_storage.rs rename to src/libstd/rt/thread_local_storage.rs index aee70980bbaf7..ee6ad8a4e08e8 100644 --- a/src/librustrt/thread_local_storage.rs +++ b/src/libstd/rt/thread_local_storage.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] #[cfg(unix)] use libc::c_int; -#[cfg(unix)] use core::ptr::null; +#[cfg(unix)] use ptr::null; #[cfg(windows)] use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; #[cfg(unix)] @@ -93,12 +93,12 @@ extern "system" { #[cfg(test)] mod test { - use std::prelude::*; + use prelude::*; use super::*; #[test] fn tls_smoke_test() { - use std::mem::transmute; + use mem::transmute; unsafe { let mut key = 0; let value = box 20i; diff --git a/src/librustrt/unwind.rs b/src/libstd/rt/unwind.rs similarity index 98% rename from src/librustrt/unwind.rs rename to src/libstd/rt/unwind.rs index 5bc542a84e6e8..1ac06270851c5 100644 --- a/src/librustrt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -59,23 +59,23 @@ use core::prelude::*; -use alloc::boxed::Box; -use collections::string::String; -use collections::str::StrAllocating; -use collections::vec::Vec; -use core::any::Any; -use core::atomic; -use core::cmp; -use core::fmt; -use core::intrinsics; -use core::mem; -use core::raw::Closure; +use boxed::Box; +use string::String; +use str::StrAllocating; +use vec::Vec; +use any::Any; +use sync::atomic; +use cmp; +use fmt; +use intrinsics; +use mem; +use raw::Closure; use libc::c_void; -use local::Local; -use task::Task; +use rt::local::Local; +use rt::task::Task; -use libunwind as uw; +use rt::libunwind as uw; #[allow(missing_copy_implementations)] pub struct Unwinder { @@ -241,7 +241,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -294,7 +294,7 @@ pub mod eabi { #[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -349,7 +349,7 @@ pub mod eabi { #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] #[doc(hidden)] pub mod eabi { - use libunwind as uw; + use rt::libunwind as uw; use libc::c_int; extern "C" { @@ -400,8 +400,7 @@ pub mod eabi { #[allow(non_camel_case_types, non_snake_case)] pub mod eabi { pub use self::EXCEPTION_DISPOSITION::*; - use core::prelude::*; - use libunwind as uw; + use rt::libunwind as uw; use libc::{c_void, c_int}; #[repr(C)] @@ -513,7 +512,7 @@ pub extern fn rust_begin_unwind(msg: &fmt::Arguments, /// the actual formatting into this shared place. #[inline(never)] #[cold] pub fn begin_unwind_fmt(msg: &fmt::Arguments, file_line: &(&'static str, uint)) -> ! { - use core::fmt::FormatWriter; + use fmt::FormatWriter; // We do two allocations here, unfortunately. But (a) they're // required with the current scheme, and (b) we don't handle diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index ce359c7b0e006..d3cfccab9d096 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -7,10 +7,18 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +// +// ignore-lexer-test FIXME #15677 + +use core::prelude::*; -use libc::uintptr_t; -use option::Option; -use option::Option::{Some, None}; +use core::cmp; +use core::fmt; +use core::intrinsics; +use core::slice; +use core::str; + +use libc::{mod, uintptr_t}; use os; use str::{FromStr, from_str, Str}; use sync::atomic; @@ -73,3 +81,136 @@ pub fn default_sched_threads() -> uint { } } } + +// Indicates whether we should perform expensive sanity checks, including rtassert! +// +// FIXME: Once the runtime matures remove the `true` below to turn off rtassert, +// etc. +pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || + cfg!(rtassert); + +pub struct Stdio(libc::c_int); + +#[allow(non_upper_case_globals)] +pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); +#[allow(non_upper_case_globals)] +pub const 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 *const libc::c_void, + data.len() as WriteLen); + } + Ok(()) // yes, we're lying + } +} + +pub fn dumb_print(args: &fmt::Arguments) { + use fmt::FormatWriter; + let mut w = Stderr; + let _ = w.write_fmt(args); +} + +pub fn abort(args: &fmt::Arguments) -> ! { + use 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 self.pos..]; + let to_write = bytes[..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: &mut msg, pos: 0 }; + let _ = write!(&mut w, "{}", args); + let msg = str::from_utf8(w.buf[mut ..w.pos]).unwrap_or("aborted"); + let msg = if msg.is_empty() {"aborted"} else {msg}; + + // 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(); } +} + +pub unsafe fn report_overflow() { + use rt::task::Task; + use rt::local::Local; + + // See the message below for why this is not emitted to the + // ^ Where did the message below go? + // task's logger. This has the additional conundrum of the + // logger may not be initialized just yet, meaning that an FFI + // call would happen to initialized it (calling out to libuv), + // and the FFI call needs 2MB of stack when we just ran out. + + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + + let name = task.and_then(|task| { + (*task).name.as_ref().map(|n| n.as_slice()) + }); + + rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("")); +} diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index 35a8713711591..862808a9e3d90 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -22,7 +22,7 @@ extern {} // LLVM implements the `frem` instruction as a call to `fmod`, which lives in // libm. Hence, we must explicitly link to it. // -// On Linux, librt and libdl are indirect dependencies via rustrt, +// On Linux, librt and libdl are indirect dependencies via std, // and binutils 2.22+ won't add them automatically #[cfg(target_os = "linux")] #[link(name = "dl")] diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs new file mode 100644 index 0000000000000..0c03060b3142d --- /dev/null +++ b/src/libstd/sys/common/backtrace.rs @@ -0,0 +1,131 @@ +// 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 io::{IoResult, Writer}; +use iter::Iterator; +use option::{Some, None}; +use result::{Ok, Err}; +use str::{StrPrelude, from_str}; +use unicode::char::UnicodeChar; + +#[cfg(target_word_size = "64")] pub const HEX_WIDTH: uint = 18; +#[cfg(target_word_size = "32")] pub const HEX_WIDTH: uint = 10; + +// All rust symbols are in theory lists of "::"-separated identifiers. Some +// assemblers, however, can't handle these characters in symbol names. To get +// around this, we use C++-style mangling. The mangling method is: +// +// 1. Prefix the symbol with "_ZN" +// 2. For each element of the path, emit the length plus the element +// 3. End the path with "E" +// +// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". +// +// We're the ones printing our backtraces, so we can't rely on anything else to +// demangle our symbols. It's *much* nicer to look at demangled symbols, so +// this function is implemented to give us nice pretty output. +// +// Note that this demangler isn't quite as fancy as it could be. We have lots +// of other information in our symbols like hashes, version, type information, +// etc. Additionally, this doesn't handle glue symbols at all. +pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { + // First validate the symbol. If it doesn't look like anything we're + // expecting, we just print it literally. Note that we must handle non-rust + // symbols because we could have any function in the backtrace. + let mut valid = true; + if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { + let mut chars = s.slice(3, s.len() - 1).chars(); + while valid { + let mut i = 0; + for c in chars { + if c.is_numeric() { + i = i * 10 + c as uint - '0' as uint; + } else { + break + } + } + if i == 0 { + valid = chars.next().is_none(); + break + } else if chars.by_ref().take(i - 1).count() != i - 1 { + valid = false; + } + } + } else { + valid = false; + } + + // Alright, let's do this. + if !valid { + try!(writer.write_str(s)); + } else { + let mut s = s.slice_from(3); + let mut first = true; + while s.len() > 1 { + if !first { + try!(writer.write_str("::")); + } else { + first = false; + } + let mut rest = s; + while rest.char_at(0).is_numeric() { + rest = rest.slice_from(1); + } + let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap(); + s = rest.slice_from(i); + rest = rest.slice_to(i); + while rest.len() > 0 { + if rest.starts_with("$") { + macro_rules! demangle( + ($($pat:expr => $demangled:expr),*) => ({ + $(if rest.starts_with($pat) { + try!(writer.write_str($demangled)); + rest = rest.slice_from($pat.len()); + } else)* + { + try!(writer.write_str(rest)); + break; + } + + }) + ) + // see src/librustc/back/link.rs for these mappings + demangle! ( + "$SP$" => "@", + "$UP$" => "Box", + "$RP$" => "*", + "$BP$" => "&", + "$LT$" => "<", + "$GT$" => ">", + "$LP$" => "(", + "$RP$" => ")", + "$C$" => ",", + + // in theory we can demangle any Unicode code point, but + // for simplicity we just catch the common ones. + "$x20" => " ", + "$x27" => "'", + "$x5b" => "[", + "$x5d" => "]" + ) + } else { + let idx = match rest.find('$') { + None => rest.len(), + Some(i) => i, + }; + try!(writer.write_str(rest.slice_to(idx))); + rest = rest.slice_from(idx); + } + } + } + } + + Ok(()) +} diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 96b4accd4bd03..ffb053e852efc 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -24,9 +24,8 @@ use prelude::*; use cell::UnsafeCell; use mem; -use rustrt::bookkeeping; -use rustrt; use sync::{StaticMutex, StaticCondvar}; +use rt::{mod, bookkeeping}; use sys::helper_signal; use task; @@ -91,7 +90,7 @@ impl Helper { self.cond.notify_one() }); - rustrt::at_exit(move|:| { self.shutdown() }); + rt::at_exit(move|:| { self.shutdown() }); *self.initialized.get() = true; } } diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 73e1c7bd9e5e0..aeee4cf01cdd0 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -19,11 +19,14 @@ use num::Int; use path::BytesContainer; use collections; +pub mod backtrace; pub mod condvar; pub mod helper_thread; pub mod mutex; pub mod net; pub mod rwlock; +pub mod stack; +pub mod thread; pub mod thread_local; // common error constructors diff --git a/src/librustrt/stack.rs b/src/libstd/sys/common/stack.rs similarity index 99% rename from src/librustrt/stack.rs rename to src/libstd/sys/common/stack.rs index 4874f642a93af..2a88e20c8fa1f 100644 --- a/src/librustrt/stack.rs +++ b/src/libstd/sys/common/stack.rs @@ -55,7 +55,7 @@ pub const RED_ZONE: uint = 20 * 1024; #[cfg(not(test))] // in testing, use the original libstd's version #[lang = "stack_exhausted"] extern fn stack_exhausted() { - use core::intrinsics; + use intrinsics; unsafe { // We're calling this function because the stack just ran out. We need @@ -100,7 +100,7 @@ extern fn stack_exhausted() { // #9854 - unwinding on windows through __morestack has never worked // #2361 - possible implementation of not using landing pads - ::stack_overflow::report(); + ::rt::util::report_overflow(); intrinsics::abort(); } diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs new file mode 100644 index 0000000000000..5e1adfb87149f --- /dev/null +++ b/src/libstd/sys/common/thread.rs @@ -0,0 +1,34 @@ +// 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 core::prelude::*; + +use boxed::Box; +use mem; +use uint; +use libc; +use sys_common::stack; +use sys::{thread, stack_overflow}; + +// This is the starting point of rust os threads. The first thing we do +// is make sure that we don't trigger __morestack (also why this has a +// no_stack_check annotation), and then we extract the main function +// and invoke it. +#[no_stack_check] +pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { + unsafe { + stack::record_os_managed_stack_bounds(0, uint::MAX); + let handler = stack_overflow::Handler::new(); + let f: Box = mem::transmute(main); + (*f)(); + drop(handler); + mem::transmute(0 as thread::rust_thread_return) + } +} diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs index cf56a71d67acd..a8bc6bf9d0d6a 100644 --- a/src/libstd/sys/common/thread_local.rs +++ b/src/libstd/sys/common/thread_local.rs @@ -58,7 +58,8 @@ use prelude::*; -use rustrt::exclusive::Exclusive; +use rt::exclusive::Exclusive; +use rt; use sync::atomic::{mod, AtomicUint}; use sync::{Once, ONCE_INIT}; @@ -283,4 +284,3 @@ mod tests { } } } - diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs new file mode 100644 index 0000000000000..c139dba2c4608 --- /dev/null +++ b/src/libstd/sys/unix/backtrace.rs @@ -0,0 +1,493 @@ +// 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. + +/// Backtrace support built on libgcc with some extra OS-specific support +/// +/// Some methods of getting a backtrace: +/// +/// * The backtrace() functions on unix. It turns out this doesn't work very +/// well for green threads on OSX, and the address to symbol portion of it +/// suffers problems that are described below. +/// +/// * Using libunwind. This is more difficult than it sounds because libunwind +/// isn't installed everywhere by default. It's also a bit of a hefty library, +/// so possibly not the best option. When testing, libunwind was excellent at +/// getting both accurate backtraces and accurate symbols across platforms. +/// This route was not chosen in favor of the next option, however. +/// +/// * We're already using libgcc_s for exceptions in rust (triggering task +/// unwinding and running destructors on the stack), and it turns out that it +/// conveniently comes with a function that also gives us a backtrace. All of +/// these functions look like _Unwind_*, but it's not quite the full +/// repertoire of the libunwind API. Due to it already being in use, this was +/// the chosen route of getting a backtrace. +/// +/// After choosing libgcc_s for backtraces, the sad part is that it will only +/// give us a stack trace of instruction pointers. Thankfully these instruction +/// pointers are accurate (they work for green and native threads), but it's +/// then up to us again to figure out how to translate these addresses to +/// symbols. As with before, we have a few options. Before, that, a little bit +/// of an interlude about symbols. This is my very limited knowledge about +/// symbol tables, and this information is likely slightly wrong, but the +/// general idea should be correct. +/// +/// When talking about symbols, it's helpful to know a few things about where +/// symbols are located. Some symbols are located in the dynamic symbol table +/// of the executable which in theory means that they're available for dynamic +/// linking and lookup. Other symbols end up only in the local symbol table of +/// the file. This loosely corresponds to pub and priv functions in Rust. +/// +/// Armed with this knowledge, we know that our solution for address to symbol +/// translation will need to consult both the local and dynamic symbol tables. +/// With that in mind, here's our options of translating an address to +/// a symbol. +/// +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() +/// behind the scenes to translate, and this is why backtrace() was not used. +/// Conveniently, this method works fantastically on OSX. It appears dladdr() +/// uses magic to consult the local symbol table, or we're putting everything +/// in the dynamic symbol table anyway. Regardless, for OSX, this is the +/// method used for translation. It's provided by the system and easy to do.o +/// +/// Sadly, all other systems have a dladdr() implementation that does not +/// consult the local symbol table. This means that most functions are blank +/// because they don't have symbols. This means that we need another solution. +/// +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the +/// libgcc_s version of the libunwind api), but involves taking a dependency +/// to libunwind. We may pursue this route in the future if we bundle +/// libunwind, but libunwind was unwieldy enough that it was not chosen at +/// this time to provide this functionality. +/// +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a +/// semi-reasonable solution. The stdlib already knows how to spawn processes, +/// so in theory it could invoke readelf, parse the output, and consult the +/// local/dynamic symbol tables from there. This ended up not getting chosen +/// due to the craziness of the idea plus the advent of the next option. +/// +/// * Use `libbacktrace`. It turns out that this is a small library bundled in +/// the gcc repository which provides backtrace and symbol translation +/// functionality. All we really need from it is the backtrace functionality, +/// and we only really need this on everything that's not OSX, so this is the +/// chosen route for now. +/// +/// In summary, the current situation uses libgcc_s to get a trace of stack +/// pointers, and we use dladdr() or libbacktrace to translate these addresses +/// to symbols. This is a bit of a hokey implementation as-is, but it works for +/// all unix platforms we support right now, so it at least gets the job done. + +use c_str::CString; +use io::{IoResult, Writer}; +use libc; +use mem; +use option::{Some, None, Option}; +use result::{Ok, Err}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + +use sys_common::backtrace::*; + +/// As always - iOS on arm uses SjLj exceptions and +/// _Unwind_Backtrace is even not available there. Still, +/// backtraces could be extracted using a backtrace function, +/// which thanks god is public +/// +/// As mentioned in a huge comment block above, backtrace doesn't +/// play well with green threads, so while it is extremely nice +/// and simple to use it should be used only on iOS devices as the +/// only viable option. +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(never)] +pub fn write(w: &mut Writer) -> IoResult<()> { + use iter::{Iterator, range}; + use result; + use slice::SliceExt; + + extern { + fn backtrace(buf: *mut *mut libc::c_void, + sz: libc::c_int) -> libc::c_int; + } + + // while it doesn't requires lock for work as everything is + // local, it still displays much nicer backtraces when a + // couple of tasks panic simultaneously + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + // 100 lines should be enough + const SIZE: uint = 100; + let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; + + // skipping the first one as it is write itself + let iter = range(1, cnt).map(|i| { + print(w, i as int, buf[i]) + }); + result::fold(iter, (), |_, _| ()) +} + +#[cfg(not(all(target_os = "ios", target_arch = "arm")))] +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn write(w: &mut Writer) -> IoResult<()> { + use io::IoError; + + struct Context<'a> { + idx: int, + writer: &'a mut Writer+'a, + last_error: Option, + } + + // When using libbacktrace, we use some necessary global state, so we + // need to prevent more than one thread from entering this block. This + // is semi-reasonable in terms of printing anyway, and we know that all + // I/O done here is blocking I/O, not green I/O, so we don't have to + // worry about this being a native vs green mutex. + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + return match unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context as *mut libc::c_void) + } { + uw::_URC_NO_REASON => { + match cx.last_error { + Some(err) => Err(err), + None => Ok(()) + } + } + _ => Ok(()), + }; + + extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx: &mut Context = unsafe { mem::transmute(arg) }; + let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; + + // Don't print out the first few frames (they're not user frames) + cx.idx += 1; + if cx.idx <= 0 { return uw::_URC_NO_REASON } + // Don't print ginormous backtraces + if cx.idx > 100 { + match write!(cx.writer, " ... \n") { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + return uw::_URC_FAILURE + } + + // Once we hit an error, stop trying to print more frames + if cx.last_error.is_some() { return uw::_URC_FAILURE } + + match print(cx.writer, cx.idx, ip) { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + + // keep going + return uw::_URC_NO_REASON + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use intrinsics; + #[repr(C)] + struct Dl_info { + dli_fname: *const libc::c_char, + dli_fbase: *mut libc::c_void, + dli_sname: *const libc::c_char, + dli_saddr: *mut libc::c_void, + } + extern { + fn dladdr(addr: *const libc::c_void, + info: *mut Dl_info) -> libc::c_int; + } + + let mut info: Dl_info = unsafe { intrinsics::init() }; + if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { + output(w, idx,addr, None) + } else { + output(w, idx, addr, Some(unsafe { + CString::new(info.dli_sname, false) + })) + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use iter::Iterator; + use os; + use path::GenericPath; + use ptr::RawPtr; + use ptr; + use slice::SliceExt; + + //////////////////////////////////////////////////////////////////////// + // libbacktrace.h API + //////////////////////////////////////////////////////////////////////// + type backtrace_syminfo_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); + type backtrace_error_callback = + extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); + enum backtrace_state {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + } + + //////////////////////////////////////////////////////////////////////// + // helper callbacks + //////////////////////////////////////////////////////////////////////// + + extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now + } + extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } + } + + // The libbacktrace API supports creating a state, but it does not + // support destroying a state. I personally take this to mean that a + // state is meant to be created and then live forever. + // + // I would love to register an at_exit() handler which cleans up this + // state, but libbacktrace provides no way to do so. + // + // With these constraints, this function has a statically cached state + // that is calculated the first time this is requested. Remember that + // backtracing all happens serially (one global lock). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; + static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") { + os::self_exe_name() + } else { + None + }; + let filename = match selfname { + Some(path) => { + let bytes = path.as_vec(); + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return STATE + } + + //////////////////////////////////////////////////////////////////////// + // translation + //////////////////////////////////////////////////////////////////////// + + // backtrace errors are currently swept under the rug, only I/O + // errors are reported + let state = unsafe { init_state() }; + if state.is_null() { + return output(w, idx, addr, None) + } + let mut data = 0 as *const libc::c_char; + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, addr as libc::uintptr_t, + syminfo_cb, error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + output(w, idx, addr, None) + } else { + output(w, idx, addr, Some(unsafe { CString::new(data, false) })) + } +} + +// Finally, after all that work above, we can emit a symbol. +fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, + s: Option) -> IoResult<()> { + try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH)); + match s.as_ref().and_then(|c| c.as_str()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "")), + } + w.write(&['\n' as u8]) +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + 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: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + + extern { + // No native _Unwind_Backtrace on iOS + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut 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(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIP(ctx: *mut _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: *mut _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, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_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(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void + { + pc + } +} diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index acbf209632630..f3babca32871a 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -34,6 +34,7 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( }; ) } +pub mod backtrace; pub mod c; pub mod ext; pub mod condvar; @@ -44,8 +45,10 @@ pub mod os; pub mod pipe; pub mod process; pub mod rwlock; +pub mod stack_overflow; pub mod sync; pub mod tcp; +pub mod thread; pub mod thread_local; pub mod timer; pub mod tty; diff --git a/src/librustrt/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs similarity index 64% rename from src/librustrt/stack_overflow.rs rename to src/libstd/sys/unix/stack_overflow.rs index 05f9f6287c404..73b98f762b479 100644 --- a/src/librustrt/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -8,20 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![allow(non_camel_case_types)] - -use core::prelude::*; use libc; -use local::Local; -use task::Task; - -pub unsafe fn init() { - imp::init(); -} +use core::prelude::*; +use self::imp::{make_handler, drop_handler}; -pub unsafe fn cleanup() { - imp::cleanup(); -} +pub use self::imp::{init, cleanup}; pub struct Handler { _data: *mut libc::c_void @@ -29,149 +20,33 @@ pub struct Handler { impl Handler { pub unsafe fn new() -> Handler { - imp::make_handler() + make_handler() } } impl Drop for Handler { fn drop(&mut self) { unsafe { - imp::drop_handler(self); - } - } -} - -pub unsafe fn report() { - // See the message below for why this is not emitted to the - // ^ Where did the message below go? - // task's logger. This has the additional conundrum of the - // logger may not be initialized just yet, meaning that an FFI - // call would happen to initialized it (calling out to libuv), - // and the FFI call needs 2MB of stack when we just ran out. - - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - - let name = task.and_then(|task| { - (*task).name.as_ref().map(|n| n.as_slice()) - }); - - rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("")); -} - -// get_task_info is called from an exception / signal handler. -// It returns the guard page of the current task or 0 if that -// guard page doesn't exist. None is returned if there's currently -// no local task. -#[cfg(any(windows, target_os = "linux", target_os = "macos"))] -unsafe fn get_task_guard_page() -> Option { - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - task.map(|task| (&*task).stack_guard().unwrap_or(0)) -} - -#[cfg(windows)] -#[allow(non_snake_case)] -mod imp { - use core::ptr; - use core::mem; - use libc; - use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; - use stack; - use super::{Handler, get_task_guard_page, report}; - - // This is initialized in init() and only read from after - static mut PAGE_SIZE: uint = 0; - - #[no_stack_check] - extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { - unsafe { - let rec = &(*(*ExceptionInfo).ExceptionRecord); - let code = rec.ExceptionCode; - - if code != EXCEPTION_STACK_OVERFLOW { - return EXCEPTION_CONTINUE_SEARCH; - } - - // We're calling into functions with stack checks, - // however stack checks by limit should be disabled on Windows - stack::record_sp_limit(0); - - if get_task_guard_page().is_some() { - report(); - } - - EXCEPTION_CONTINUE_SEARCH - } - } - - pub unsafe fn init() { - let mut info = mem::zeroed(); - libc::GetSystemInfo(&mut info); - PAGE_SIZE = info.dwPageSize as uint; - - if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { - panic!("failed to install exception handler"); + drop_handler(self); } - - mem::forget(make_handler()); - } - - pub unsafe fn cleanup() { - } - - pub unsafe fn make_handler() -> Handler { - if SetThreadStackGuarantee(&mut 0x5000) == 0 { - panic!("failed to reserve stack space for exception handling"); - } - - super::Handler { _data: 0i as *mut libc::c_void } - } - - pub unsafe fn drop_handler(_handler: &mut Handler) { - } - - pub struct EXCEPTION_RECORD { - pub ExceptionCode: DWORD, - pub ExceptionFlags: DWORD, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: LPVOID, - pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] - } - - pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: LPVOID - } - - pub type PVECTORED_EXCEPTION_HANDLER = extern "system" - fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; - - pub type ULONG = libc::c_ulong; - - const EXCEPTION_CONTINUE_SEARCH: LONG = 0; - const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; - const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; - - extern "system" { - fn AddVectoredExceptionHandler(FirstHandler: ULONG, - VectoredHandler: PVECTORED_EXCEPTION_HANDLER) - -> LPVOID; - fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; } } #[cfg(any(target_os = "linux", target_os = "macos"))] mod imp { use core::prelude::*; - use stack; + use sys_common::stack; - use super::{Handler, get_task_guard_page, report}; - use core::mem; - use core::ptr; - use core::intrinsics; + use super::Handler; + use rt::util::report_overflow; + use mem; + use ptr; + use intrinsics; use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, SA_SIGINFO, SA_ONSTACK, sigaltstack, SIGSTKSZ}; + use rt::local::Local; + use rt::task::Task; use libc; use libc::funcs::posix88::mman::{mmap, munmap}; use libc::consts::os::posix88::{SIGSEGV, @@ -185,6 +60,16 @@ mod imp { // This is initialized in init() and only read from after static mut PAGE_SIZE: uint = 0; + // get_task_info is called from an exception / signal handler. + // It returns the guard page of the current task or 0 if that + // guard page doesn't exist. None is returned if there's currently + // no local task. + unsafe fn get_task_guard_page() -> Option { + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + task.map(|task| (&*task).stack_guard().unwrap_or(0)) + } + + #[no_stack_check] unsafe extern fn signal_handler(signum: libc::c_int, info: *mut siginfo, @@ -212,7 +97,7 @@ mod imp { term(signum); } - report(); + report_overflow(); intrinsics::abort() } @@ -387,8 +272,7 @@ mod imp { } #[cfg(not(any(target_os = "linux", - target_os = "macos", - windows)))] + target_os = "macos")))] mod imp { use libc; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs new file mode 100644 index 0000000000000..02da3a1981837 --- /dev/null +++ b/src/libstd/sys/unix/thread.rs @@ -0,0 +1,270 @@ +// 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 core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; +use libc; + +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = libc::pthread_t; +pub type rust_thread_return = *mut u8; +pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))] +pub mod guard { + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod guard { + use super::*; + #[cfg(any(target_os = "linux", target_os = "android"))] + use mem; + #[cfg(any(target_os = "linux", target_os = "android"))] + use ptr; + use libc; + use libc::funcs::posix88::mman::{mmap}; + use libc::consts::os::posix88::{PROT_NONE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED, + MAP_FIXED}; + + // These are initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + static mut GUARD_PAGE: uint = 0; + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> *mut libc::c_void { + current() as *mut libc::c_void + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + unsafe fn get_stack_start() -> *mut libc::c_void { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + stackaddr + } + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let stackaddr = get_stack_start(); + + // Rellocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + let result = mmap(stackaddr, + PAGE_SIZE as libc::size_t, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0); + + if result != stackaddr || result == MAP_FAILED { + panic!("failed to allocate a guard page"); + } + + let offset = if cfg!(target_os = "linux") { + 2 + } else { + 1 + }; + + GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; + } + + pub unsafe fn main() -> uint { + GUARD_PAGE + } + + #[cfg(target_os = "macos")] + pub unsafe fn current() -> uint { + (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - + pthread_get_stacksize_np(pthread_self())) as uint + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub unsafe fn current() -> uint { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut guardsize = 0; + if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { + panic!("failed to get stack guard page"); + } + if guardsize == 0 { + panic!("there is no guard page"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + + stackaddr as uint + guardsize as uint + } +} + +pub unsafe fn create(stack: uint, p: Box) -> rust_thread { + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(pthread_attr_init(&mut attr), 0); + assert_eq!(pthread_attr_setdetachstate(&mut attr, + PTHREAD_CREATE_JOINABLE), 0); + + // Reserve room for the red zone, the runtime's stack of last resort. + let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); + match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { + 0 => { + }, + libc::EINVAL => { + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint; + let stack_size = (stack_size + page_size - 1) & + (-(page_size as int - 1) as uint - 1); + assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); + }, + errno => { + // This cannot really happen. + panic!("pthread_attr_setstacksize() error: {}", errno); + }, + }; + + let arg: *mut libc::c_void = mem::transmute(p); + let ret = pthread_create(&mut native, &attr, thread_start, arg); + assert_eq!(pthread_attr_destroy(&mut attr), 0); + + if ret != 0 { + // be sure to not leak the closure + let _p: Box = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + native +} + +pub unsafe fn join(native: rust_thread) { + assert_eq!(pthread_join(native, ptr::null_mut()), 0); +} + +pub unsafe fn detach(native: rust_thread) { + assert_eq!(pthread_detach(native), 0); +} + +pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } +// glibc >= 2.15 has a __pthread_get_minstack() function that returns +// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local +// storage. We need that information to avoid blowing up when a small stack +// is created in an application with big thread-local storage requirements. +// See #6233 for rationale and details. +// +// Link weakly to the symbol for compatibility with older versions of glibc. +// Assumes that we've been dynamically linked to libpthread but that is +// currently always the case. Note that you need to check that the symbol +// is non-null before calling it! +#[cfg(target_os = "linux")] +fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t { + type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; + extern { + #[linkage = "extern_weak"] + static __pthread_get_minstack: *const (); + } + if __pthread_get_minstack.is_null() { + PTHREAD_STACK_MIN + } else { + unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) } + } +} + +// __pthread_get_minstack() is marked as weak but extern_weak linkage is +// not supported on OS X, hence this kludge... +#[cfg(not(target_os = "linux"))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { + PTHREAD_STACK_MIN +} + +#[cfg(any(target_os = "linux"))] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_getattr_np(native: libc::pthread_t, + attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, + guardsize: *mut libc::size_t) -> libc::c_int; + pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, + stackaddr: *mut *mut libc::c_void, + stacksize: *mut libc::size_t) -> libc::c_int; +} + +#[cfg(target_os = "macos")] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; + pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; +} + +extern { + fn pthread_create(native: *mut libc::pthread_t, + attr: *const libc::pthread_attr_t, + f: StartFn, + value: *mut libc::c_void) -> libc::c_int; + fn pthread_join(native: libc::pthread_t, + value: *mut *mut libc::c_void) -> libc::c_int; + fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int; + fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, + state: libc::c_int) -> libc::c_int; + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + fn sched_yield() -> libc::c_int; +} diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs new file mode 100644 index 0000000000000..833b69d6cbebb --- /dev/null +++ b/src/libstd/sys/windows/backtrace.rs @@ -0,0 +1,371 @@ +// 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. +/// As always, windows has something very different than unix, we mainly want +/// to avoid having to depend too much on libunwind for windows. +/// +/// If you google around, you'll find a fair bit of references to built-in +/// functions to get backtraces on windows. It turns out that most of these are +/// in an external library called dbghelp. I was unable to find this library +/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +/// of it. +/// +/// You'll also find that there's a function called CaptureStackBackTrace +/// mentioned frequently (which is also easy to use), but sadly I didn't have a +/// copy of that function in my mingw install (maybe it was broken?). Instead, +/// this takes the route of using StackWalk64 in order to walk the stack. + +use c_str::CString; +use intrinsics; +use io::{IoResult, Writer}; +use libc; +use mem; +use ops::Drop; +use option::{Some, None}; +use path::Path; +use result::{Ok, Err}; +use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use slice::SliceExt; +use str::StrPrelude; +use dynamic_lib::DynamicLibrary; + +use sys_common::backtrace::*; + +#[allow(non_snake_case)] +extern "system" { + fn GetCurrentProcess() -> libc::HANDLE; + fn GetCurrentThread() -> libc::HANDLE; + fn RtlCaptureContext(ctx: *mut arch::CONTEXT); +} + +type SymFromAddrFn = + extern "system" fn(libc::HANDLE, u64, *mut u64, + *mut SYMBOL_INFO) -> libc::BOOL; +type SymInitializeFn = + extern "system" fn(libc::HANDLE, *mut libc::c_void, + libc::BOOL) -> libc::BOOL; +type SymCleanupFn = + extern "system" fn(libc::HANDLE) -> libc::BOOL; + +type StackWalk64Fn = + extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, + *mut STACKFRAME64, *mut arch::CONTEXT, + *mut libc::c_void, *mut libc::c_void, + *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; + +const MAX_SYM_NAME: uint = 2000; +const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; +const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; +const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + +#[repr(C)] +struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], +} + + +#[repr(C)] +enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +struct ADDRESS64 { + Offset: u64, + Segment: u16, + Mode: ADDRESS_MODE, +} + +struct STACKFRAME64 { + AddrPC: ADDRESS64, + AddrReturn: ADDRESS64, + AddrFrame: ADDRESS64, + AddrStack: ADDRESS64, + AddrBStore: ADDRESS64, + FuncTableEntry: *mut libc::c_void, + Params: [u64, ..4], + Far: libc::BOOL, + Virtual: libc::BOOL, + Reserved: [u64, ..3], + KdHelp: KDHELP64, +} + +struct KDHELP64 { + Thread: u64, + ThCallbackStack: libc::DWORD, + ThCallbackBStore: libc::DWORD, + NextCallback: libc::DWORD, + FramePointer: libc::DWORD, + KiCallUserMode: u64, + KeUserCallbackDispatcher: u64, + SystemRangeStart: u64, + KiUserExceptionDispatcher: u64, + StackBase: u64, + StackLimit: u64, + Reserved: [u64, ..5], +} + +#[cfg(target_arch = "x86")] +mod arch { + use libc; + + const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + + #[repr(C)] + pub struct CONTEXT { + ContextFlags: libc::DWORD, + Dr0: libc::DWORD, + Dr1: libc::DWORD, + Dr2: libc::DWORD, + Dr3: libc::DWORD, + Dr6: libc::DWORD, + Dr7: libc::DWORD, + FloatSave: FLOATING_SAVE_AREA, + SegGs: libc::DWORD, + SegFs: libc::DWORD, + SegEs: libc::DWORD, + SegDs: libc::DWORD, + Edi: libc::DWORD, + Esi: libc::DWORD, + Ebx: libc::DWORD, + Edx: libc::DWORD, + Ecx: libc::DWORD, + Eax: libc::DWORD, + Ebp: libc::DWORD, + Eip: libc::DWORD, + SegCs: libc::DWORD, + EFlags: libc::DWORD, + Esp: libc::DWORD, + SegSs: libc::DWORD, + ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + ControlWord: libc::DWORD, + StatusWord: libc::DWORD, + TagWord: libc::DWORD, + ErrorOffset: libc::DWORD, + ErrorSelector: libc::DWORD, + DataOffset: libc::DWORD, + DataSelector: libc::DWORD, + RegisterArea: [u8, ..80], + Cr0NpxState: libc::DWORD, + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> libc::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_I386 + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::{c_longlong, c_ulonglong}; + use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + use simd; + + #[repr(C)] + pub struct CONTEXT { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + P1Home: DWORDLONG, + P2Home: DWORDLONG, + P3Home: DWORDLONG, + P4Home: DWORDLONG, + P5Home: DWORDLONG, + P6Home: DWORDLONG, + + ContextFlags: DWORD, + MxCsr: DWORD, + + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + + Dr0: DWORDLONG, + Dr1: DWORDLONG, + Dr2: DWORDLONG, + Dr3: DWORDLONG, + Dr6: DWORDLONG, + Dr7: DWORDLONG, + + Rax: DWORDLONG, + Rcx: DWORDLONG, + Rdx: DWORDLONG, + Rbx: DWORDLONG, + Rsp: DWORDLONG, + Rbp: DWORDLONG, + Rsi: DWORDLONG, + Rdi: DWORDLONG, + R8: DWORDLONG, + R9: DWORDLONG, + R10: DWORDLONG, + R11: DWORDLONG, + R12: DWORDLONG, + R13: DWORDLONG, + R14: DWORDLONG, + R15: DWORDLONG, + + Rip: DWORDLONG, + + FltSave: FLOATING_SAVE_AREA, + + VectorRegister: [M128A, .. 26], + VectorControl: DWORDLONG, + + DebugControl: DWORDLONG, + LastBranchToRip: DWORDLONG, + LastBranchFromRip: DWORDLONG, + LastExceptionToRip: DWORDLONG, + LastExceptionFromRip: DWORDLONG, + } + + #[repr(C)] + pub struct M128A { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + Low: c_ulonglong, + High: c_longlong + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + _Dummy: [u8, ..512] // FIXME: Fill this out + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_AMD64 + } +} + +#[repr(C)] +struct Cleanup { + handle: libc::HANDLE, + SymCleanup: SymCleanupFn, +} + +impl Drop for Cleanup { + fn drop(&mut self) { (self.SymCleanup)(self.handle); } +} + +pub fn write(w: &mut Writer) -> IoResult<()> { + // According to windows documentation, all dbghelp functions are + // single-threaded. + static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + // Open up dbghelp.dll, we don't link to it explicitly because it can't + // always be found. Additionally, it's nice having fewer dependencies. + let path = Path::new("dbghelp.dll"); + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(..) => return Ok(()), + }; + + macro_rules! sym( ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } + }) ) + + // Fetch the symbols necessary from dbghelp.dll + let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); + let SymInitialize = sym!("SymInitialize", SymInitializeFn); + let SymCleanup = sym!("SymCleanup", SymCleanupFn); + let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = unsafe { GetCurrentProcess() }; + let thread = unsafe { GetCurrentThread() }; + let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; + unsafe { RtlCaptureContext(&mut context); } + let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; + let image = arch::init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); + if ret != libc::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0i; + try!(write!(w, "stack backtrace:\n")); + while StackWalk64(image, process, thread, &mut frame, &mut context, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void) == libc::TRUE{ + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, + &mut info); + + if ret == libc::TRUE { + try!(write!(w, " - ")); + let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; + let bytes = cstr.as_bytes(); + match cstr.as_str() { + Some(s) => try!(demangle(w, s)), + None => try!(w.write(bytes[..bytes.len()-1])), + } + } + try!(w.write(&['\n' as u8])); + } + + Ok(()) +} diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index d22d4e0f534db..6924687d8c470 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -35,6 +35,7 @@ macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( }; ) } +pub mod backtrace; pub mod c; pub mod ext; pub mod condvar; @@ -46,7 +47,9 @@ pub mod pipe; pub mod process; pub mod rwlock; pub mod sync; +pub mod stack_overflow; pub mod tcp; +pub mod thread; pub mod thread_local; pub mod timer; pub mod tty; diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs new file mode 100644 index 0000000000000..e3d96a054f4c6 --- /dev/null +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -0,0 +1,120 @@ +// 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 rt::local::Local; +use rt::task::Task; +use rt::util::report_overflow; +use core::prelude::*; +use ptr; +use mem; +use libc; +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use sys_common::stack; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) {} +} + +// get_task_info is called from an exception / signal handler. +// It returns the guard page of the current task or 0 if that +// guard page doesn't exist. None is returned if there's currently +// no local task. +unsafe fn get_task_guard_page() -> Option { + let task: Option<*mut Task> = Local::try_unsafe_borrow(); + task.map(|task| (&*task).stack_guard().unwrap_or(0)) +} + +// This is initialized in init() and only read from after +static mut PAGE_SIZE: uint = 0; + +#[no_stack_check] +extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code != EXCEPTION_STACK_OVERFLOW { + return EXCEPTION_CONTINUE_SEARCH; + } + + // We're calling into functions with stack checks, + // however stack checks by limit should be disabled on Windows + stack::record_sp_limit(0); + + if get_task_guard_page().is_some() { + report_overflow(); + } + + EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + PAGE_SIZE = info.dwPageSize as uint; + + if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { + panic!("failed to install exception handler"); + } + + mem::forget(make_handler()); +} + +pub unsafe fn cleanup() { +} + +pub unsafe fn make_handler() -> Handler { + if SetThreadStackGuarantee(&mut 0x5000) == 0 { + panic!("failed to reserve stack space for exception handling"); + } + + Handler { _data: 0i as *mut libc::c_void } +} + +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] +} + +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: LPVOID +} + +pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + +pub type ULONG = libc::c_ulong; + +const EXCEPTION_CONTINUE_SEARCH: LONG = 0; +const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; +const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + +extern "system" { + fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; +} diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs new file mode 100644 index 0000000000000..00f1e9767f5fa --- /dev/null +++ b/src/libstd/sys/windows/thread.rs @@ -0,0 +1,95 @@ +// 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 core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc; +use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = HANDLE; +pub type rust_thread_return = DWORD; + +pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +pub mod guard { + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +pub unsafe fn create(stack: uint, p: Box) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(p); + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's + // just that below a certain threshold you can't do anything useful. + // That threshold is application and architecture-specific, however. + // For now, the only requirement is that it's big enough to hold the + // red zone. Round up to the next 64 kB because that's what the NT + // kernel does, might as well make it explicit. With the current + // 20 kB red zone, that makes for a 64 kB minimum stack. + let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); + let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, arg, 0, ptr::null_mut()); + + if ret as uint == 0 { + // be sure to not leak the closure + let _p: Box = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + return ret; +} + +pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); +} + +pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); +} + +pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); +} + +#[allow(non_snake_case)] +extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; +} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 969b322af991f..6c8d9639d5c4c 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -13,8 +13,8 @@ use prelude::*; use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use mem; -use rustrt; -use rustrt::exclusive::Exclusive; +use rt; +use rt::exclusive::Exclusive; use sync::{ONCE_INIT, Once}; pub type Key = DWORD; @@ -131,7 +131,7 @@ fn init_dtors() { DTORS = mem::transmute(dtors); } - rustrt::at_exit(move|| unsafe { + rt::at_exit(move|| unsafe { mem::transmute::<_, Box>>>(DTORS); DTORS = 0 as *mut _; }); diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 324b594209a1e..127cad186f693 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -53,9 +53,9 @@ use kinds::Send; use option::Option; use option::Option::{None, Some}; use result::Result; -use rustrt::local::Local; -use rustrt::task::Task; -use rustrt::task; +use rt::local::Local; +use rt::task; +use rt::task::Task; use str::SendStr; use string::{String, ToString}; use thunk::{Thunk}; @@ -252,7 +252,7 @@ pub fn try_future(f: F) -> Future>> /// Read the name of the current task. #[stable] pub fn name() -> Option { - use rustrt::task::Task; + use rt::task::Task; let task = Local::borrow(None::); match task.name { @@ -264,7 +264,7 @@ pub fn name() -> Option { /// Yield control to the task scheduler. #[unstable = "Name will change."] pub fn deschedule() { - use rustrt::task::Task; + use rt::task::Task; Task::yield_now(); } @@ -272,7 +272,7 @@ pub fn deschedule() { /// destructor that is run while unwinding the stack after a call to `panic!()`). #[unstable = "May move to a different module."] pub fn failing() -> bool { - use rustrt::task::Task; + use rt::task::Task; Local::borrow(None::).unwinder.unwinding() } diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index 1268ab8e0cfa6..b78428d69de21 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -446,7 +446,7 @@ mod tests { use prelude::*; use cell::UnsafeCell; - use rustrt::thread::Thread; + use rt::thread::Thread; struct Foo(Sender<()>); diff --git a/src/librustrt/thunk.rs b/src/libstd/thunk.rs similarity index 100% rename from src/librustrt/thunk.rs rename to src/libstd/thunk.rs diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll index 33d2d31a2e0c1..18b23b4143a58 100644 --- a/src/rt/rust_try.ll +++ b/src/rt/rust_try.ll @@ -12,7 +12,7 @@ ; When f(...) returns normally, the return value is null. ; When f(...) throws, the return value is a pointer to the caught exception object. -; See also: librustrt/unwind.rs +; See also: libstd/rt/unwind.rs define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) { diff --git a/src/test/run-pass/foreign-call-no-runtime.rs b/src/test/run-pass/foreign-call-no-runtime.rs index 3f659f1dbfbf7..0bfae8498e2a6 100644 --- a/src/test/run-pass/foreign-call-no-runtime.rs +++ b/src/test/run-pass/foreign-call-no-runtime.rs @@ -9,10 +9,9 @@ // except according to those terms. extern crate libc; -extern crate rustrt; use std::mem; -use rustrt::thread::Thread; +use std::rt::thread::Thread; #[link(name = "rust_test_helpers")] extern { diff --git a/src/test/run-pass/match-ref-binding-in-guard-3256.rs b/src/test/run-pass/match-ref-binding-in-guard-3256.rs index ac783961b508e..9a822ddbbcc51 100644 --- a/src/test/run-pass/match-ref-binding-in-guard-3256.rs +++ b/src/test/run-pass/match-ref-binding-in-guard-3256.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; +use std::rt::exclusive; pub fn main() { unsafe { - let x = Some(::rustrt::exclusive::Exclusive::new(true)); + let x = Some(exclusive::Exclusive::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock()); diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index 36b90a9168f71..067b0e8dfb4a2 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -8,15 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; - use std::io::process::{Command, ProcessOutput}; use std::os; use std::str; use std::rt; use std::thunk::Thunk; -use rustrt::unwind::try; +use std::rt::unwind::try; #[start] fn start(argc: int, argv: *const *const u8) -> int { diff --git a/src/test/run-pass/writealias.rs b/src/test/run-pass/writealias.rs index c8d281a791c90..a86aedd757a30 100644 --- a/src/test/run-pass/writealias.rs +++ b/src/test/run-pass/writealias.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -extern crate rustrt; +use std::rt::exclusive; struct Point {x: int, y: int, z: int} @@ -16,7 +16,7 @@ fn f(p: &mut Point) { p.z = 13; } pub fn main() { unsafe { - let x = Some(::rustrt::exclusive::Exclusive::new(true)); + let x = Some(exclusive::Exclusive::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock()); From 74d07699938b846703ab552f52cd5c32f751900f Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Nov 2014 16:21:39 -0800 Subject: [PATCH 02/24] Refactor std::os to use sys::os --- src/libstd/os.rs | 406 +++-------------------------------- src/libstd/sys/unix/os.rs | 135 +++++++++++- src/libstd/sys/windows/os.rs | 198 ++++++++++++++++- 3 files changed, 352 insertions(+), 387 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 6e02c03602f1c..550e9e06aae01 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -46,7 +46,6 @@ use option::Option::{Some, None}; use os; use path::{Path, GenericPath, BytesContainer}; use sys; -use sys::os as os_imp; use ptr::RawPtr; use ptr; use result::Result; @@ -78,7 +77,6 @@ pub fn num_cpus() -> uint { } pub const TMPBUF_SZ : uint = 1000u; -const BUF_BYTES : uint = 2048u; /// Returns the current working directory as a `Path`. /// @@ -96,118 +94,12 @@ const BUF_BYTES : uint = 2048u; /// ```rust /// use std::os; /// -/// // We assume that we are in a valid directory like "/home". +/// // We assume that we are in a valid directory. /// let current_working_directory = os::getcwd().unwrap(); /// println!("The current directory is {}", current_working_directory.display()); -/// // /home /// ``` -#[cfg(unix)] -pub fn getcwd() -> IoResult { - use c_str::CString; - - let mut buf = [0 as c_char, ..BUF_BYTES]; - unsafe { - if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { - Err(IoError::last_error()) - } else { - Ok(Path::new(CString::new(buf.as_ptr(), false))) - } - } -} - -/// Returns the current working directory as a `Path`. -/// -/// # Errors -/// -/// Returns an `Err` if the current working directory value is invalid. -/// Possible cases: -/// -/// * Current directory does not exist. -/// * There are insufficient permissions to access the current directory. -/// * The internal buffer is not large enough to hold the path. -/// -/// # Example -/// -/// ```rust -/// use std::os; -/// -/// // We assume that we are in a valid directory like "C:\\Windows". -/// let current_working_directory = os::getcwd().unwrap(); -/// println!("The current directory is {}", current_working_directory.display()); -/// // C:\\Windows -/// ``` -#[cfg(windows)] pub fn getcwd() -> IoResult { - use libc::DWORD; - use libc::GetCurrentDirectoryW; - use io::OtherIoError; - - let mut buf = [0 as u16, ..BUF_BYTES]; - unsafe { - if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { - return Err(IoError::last_error()); - } - } - - match String::from_utf16(::str::truncate_utf16_at_nul(&buf)) { - Some(ref cwd) => Ok(Path::new(cwd)), - None => Err(IoError { - kind: OtherIoError, - desc: "GetCurrentDirectoryW returned invalid UTF-16", - detail: None, - }), - } -} - -#[cfg(windows)] -pub mod windoze { - use libc::types::os::arch::extra::DWORD; - use libc; - use ops::FnMut; - use option::Option; - use option::Option::None; - use option; - use os::TMPBUF_SZ; - use slice::SliceExt; - use string::String; - use str::StrPrelude; - use vec::Vec; - - pub fn fill_utf16_buf_and_decode(mut f: F) -> Option where - F: FnMut(*mut u16, DWORD) -> DWORD, - { - - unsafe { - let mut n = TMPBUF_SZ as DWORD; - let mut res = None; - let mut done = false; - while !done { - let mut buf = Vec::from_elem(n as uint, 0u16); - let k = f(buf.as_mut_ptr(), n); - if k == (0 as DWORD) { - done = true; - } else if k == n && - libc::GetLastError() == - libc::ERROR_INSUFFICIENT_BUFFER as DWORD { - n *= 2 as DWORD; - } else if k >= n { - n = k; - } else { - done = true; - } - if k != 0 && done { - let sub = buf.slice(0, k as uint); - // We want to explicitly catch the case when the - // closure returned invalid UTF-16, rather than - // set `res` to None and continue. - let s = String::from_utf16(sub) - .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); - res = option::Option::Some(s) - } - } - return res; - } - } + sys::os::getcwd() } /* @@ -253,71 +145,6 @@ pub fn env() -> Vec<(String,String)> { /// environment variables of the current process. pub fn env_as_bytes() -> Vec<(Vec,Vec)> { unsafe { - #[cfg(windows)] - unsafe fn get_env_pairs() -> Vec> { - use slice; - - use libc::funcs::extra::kernel32::{ - GetEnvironmentStringsW, - FreeEnvironmentStringsW - }; - let ch = GetEnvironmentStringsW(); - if ch as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); - } - // Here, we lossily decode the string as UTF16. - // - // The docs suggest that the result should be in Unicode, but - // Windows doesn't guarantee it's actually UTF16 -- it doesn't - // validate the environment string passed to CreateProcess nor - // SetEnvironmentVariable. Yet, it's unlikely that returning a - // raw u16 buffer would be of practical use since the result would - // be inherently platform-dependent and introduce additional - // complexity to this code. - // - // Using the non-Unicode version of GetEnvironmentStrings is even - // worse since the result is in an OEM code page. Characters that - // can't be encoded in the code page would be turned into question - // marks. - let mut result = Vec::new(); - let mut i = 0; - while *ch.offset(i) != 0 { - let p = &*ch.offset(i); - let mut len = 0; - while *(p as *const _).offset(len) != 0 { - len += 1; - } - let p = p as *const u16; - let s = slice::from_raw_buf(&p, len as uint); - result.push(String::from_utf16_lossy(s).into_bytes()); - i += len as int + 1; - } - FreeEnvironmentStringsW(ch); - result - } - #[cfg(unix)] - unsafe fn get_env_pairs() -> Vec> { - use c_str::CString; - - extern { - fn rust_env_pairs() -> *const *const c_char; - } - let mut environ = rust_env_pairs(); - if environ as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); - } - let mut result = Vec::new(); - while *environ != 0 as *const _ { - let env_pair = - CString::new(*environ, false).as_bytes_no_nul().to_vec(); - result.push(env_pair); - environ = environ.offset(1); - } - result - } - fn env_convert(input: Vec>) -> Vec<(Vec, Vec)> { let mut pairs = Vec::new(); for p in input.iter() { @@ -330,7 +157,7 @@ pub fn env_as_bytes() -> Vec<(Vec,Vec)> { pairs } with_env_lock(|| { - let unparsed_environ = get_env_pairs(); + let unparsed_environ = sys::os::get_env_pairs(); env_convert(unparsed_environ) }) } @@ -390,7 +217,7 @@ pub fn getenv_as_bytes(n: &str) -> Option> { pub fn getenv(n: &str) -> Option { unsafe { with_env_lock(|| { - use os::windoze::{fill_utf16_buf_and_decode}; + use sys::os::fill_utf16_buf_and_decode; let mut n: Vec = n.utf16_units().collect(); n.push(0); fill_utf16_buf_and_decode(|buf, sz| { @@ -506,52 +333,7 @@ pub fn unsetenv(n: &str) { /// } /// ``` pub fn split_paths(unparsed: T) -> Vec { - #[cfg(unix)] - fn _split_paths(unparsed: T) -> Vec { - unparsed.container_as_bytes() - .split(|b| *b == b':') - .map(Path::new) - .collect() - } - - #[cfg(windows)] - fn _split_paths(unparsed: T) -> Vec { - // On Windows, the PATH environment variable is semicolon separated. Double - // quotes are used as a way of introducing literal semicolons (since - // c:\some;dir is a valid Windows path). Double quotes are not themselves - // permitted in path names, so there is no way to escape a double quote. - // Quoted regions can appear in arbitrary locations, so - // - // c:\foo;c:\som"e;di"r;c:\bar - // - // Should parse as [c:\foo, c:\some;dir, c:\bar]. - // - // (The above is based on testing; there is no clear reference available - // for the grammar.) - - let mut parsed = Vec::new(); - let mut in_progress = Vec::new(); - let mut in_quote = false; - - for b in unparsed.container_as_bytes().iter() { - match *b { - b';' if !in_quote => { - parsed.push(Path::new(in_progress.as_slice())); - in_progress.truncate(0) - } - b'"' => { - in_quote = !in_quote; - } - _ => { - in_progress.push(*b); - } - } - } - parsed.push(Path::new(in_progress)); - parsed - } - - _split_paths(unparsed) + sys::os::split_paths(unparsed.container_as_bytes()) } /// Joins a collection of `Path`s appropriately for the `PATH` @@ -576,42 +358,7 @@ pub fn split_paths(unparsed: T) -> Vec { /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap()); /// ``` pub fn join_paths(paths: &[T]) -> Result, &'static str> { - #[cfg(windows)] - fn _join_paths(paths: &[T]) -> Result, &'static str> { - let mut joined = Vec::new(); - let sep = b';'; - - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { - if i > 0 { joined.push(sep) } - if path.contains(&b'"') { - return Err("path segment contains `\"`"); - } else if path.contains(&sep) { - joined.push(b'"'); - joined.push_all(path); - joined.push(b'"'); - } else { - joined.push_all(path); - } - } - - Ok(joined) - } - - #[cfg(unix)] - fn _join_paths(paths: &[T]) -> Result, &'static str> { - let mut joined = Vec::new(); - let sep = b':'; - - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { - if i > 0 { joined.push(sep) } - if path.contains(&sep) { return Err("path segment contains separator `:`") } - joined.push_all(path); - } - - Ok(joined) - } - - _join_paths(paths) + sys::os::join_paths(paths) } /// A low-level OS in-memory pipe. @@ -664,69 +411,7 @@ pub fn dll_filename(base: &str) -> String { /// }; /// ``` pub fn self_exe_name() -> Option { - - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - fn load_self() -> Option> { - unsafe { - use libc::funcs::bsd44::*; - use libc::consts::os::extra::*; - let mut mib = vec![CTL_KERN as c_int, - KERN_PROC as c_int, - KERN_PROC_PATHNAME as c_int, - -1 as c_int]; - let mut sz: libc::size_t = 0; - let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, - ptr::null_mut(), &mut sz, ptr::null_mut(), - 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } - let mut v: Vec = Vec::with_capacity(sz as uint); - let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, - v.as_mut_ptr() as *mut c_void, &mut sz, - ptr::null_mut(), 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } - v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) - } - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - fn load_self() -> Option> { - use std::io; - - match io::fs::readlink(&Path::new("/proc/self/exe")) { - Ok(path) => Some(path.into_vec()), - Err(..) => None - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn load_self() -> Option> { - unsafe { - use libc::funcs::extra::_NSGetExecutablePath; - let mut sz: u32 = 0; - _NSGetExecutablePath(ptr::null_mut(), &mut sz); - if sz == 0 { return None; } - let mut v: Vec = Vec::with_capacity(sz as uint); - let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); - if err != 0 { return None; } - v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) - } - } - - #[cfg(windows)] - fn load_self() -> Option> { - unsafe { - use os::windoze::fill_utf16_buf_and_decode; - fill_utf16_buf_and_decode(|buf, sz| { - libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) - }).map(|s| s.into_string().into_bytes()) - } - } - - load_self().and_then(Path::new_opt) + sys::os::load_self().and_then(Path::new_opt) } /// Optionally returns the filesystem path to the current executable which is @@ -842,7 +527,6 @@ pub fn tmpdir() -> Path { } } -/// /// Convert a relative path to an absolute path /// /// If the given path is relative, return it prepended with the current working @@ -887,37 +571,12 @@ pub fn make_absolute(p: &Path) -> IoResult { /// println!("Successfully changed working directory to {}!", root.display()); /// ``` pub fn change_dir(p: &Path) -> IoResult<()> { - return chdir(p); - - #[cfg(windows)] - fn chdir(p: &Path) -> IoResult<()> { - let mut p = p.as_str().unwrap().utf16_units().collect::>(); - p.push(0); - - unsafe { - match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) { - true => Ok(()), - false => Err(IoError::last_error()), - } - } - } - - #[cfg(unix)] - fn chdir(p: &Path) -> IoResult<()> { - p.with_c_str(|buf| { - unsafe { - match libc::chdir(buf) == (0 as c_int) { - true => Ok(()), - false => Err(IoError::last_error()), - } - } - }) - } + return sys::os::chdir(p); } /// Returns the platform-specific value of errno pub fn errno() -> uint { - os_imp::errno() as uint + sys::os::errno() as uint } /// Return the string corresponding to an `errno()` value of `errnum`. @@ -930,7 +589,7 @@ pub fn errno() -> uint { /// println!("{}", os::error_string(os::errno() as uint)); /// ``` pub fn error_string(errnum: uint) -> String { - return os_imp::error_string(errnum as i32); + return sys::os::error_string(errnum as i32); } /// Get a string representing the platform-dependent last error @@ -1144,38 +803,9 @@ extern { pub fn _NSGetArgv() -> *mut *mut *mut c_char; } -// Round up `from` to be divisible by `to` -fn round_up(from: uint, to: uint) -> uint { - let r = if from % to == 0 { - from - } else { - from + to - (from % to) - }; - if r == 0 { - to - } else { - r - } -} - /// Returns the page size of the current architecture in bytes. -#[cfg(unix)] pub fn page_size() -> uint { - unsafe { - libc::sysconf(libc::_SC_PAGESIZE) as uint - } -} - -/// Returns the page size of the current architecture in bytes. -#[cfg(windows)] -pub fn page_size() -> uint { - use mem; - unsafe { - let mut info = mem::zeroed(); - libc::GetSystemInfo(&mut info); - - return info.dwPageSize as uint; - } + sys::os::page_size() } /// A memory mapped file or chunk of memory. This is a very system-specific @@ -1325,6 +955,20 @@ impl FromError for Box { } } +// Round up `from` to be divisible by `to` +fn round_up(from: uint, to: uint) -> uint { + let r = if from % to == 0 { + from + } else { + from + to - (from % to) + }; + if r == 0 { + to + } else { + r + } +} + #[cfg(unix)] impl MemoryMap { /// Create a new mapping with the given `options`, at least `min_len` bytes diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index d951977fa5943..0ed079df55b35 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -8,14 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use libc; -use libc::{c_int, c_char}; +//! Implementation of `std::os` functionality for unix systems + use prelude::*; -use io::IoResult; + +use error::{FromError, Error}; +use fmt; +use io::{IoError, IoResult}; +use libc::{mod, c_int, c_char, c_void}; +use path::{Path, GenericPath, BytesContainer}; +use ptr::{mod, RawPtr}; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use sys::fs::FileDesc; +use os; use os::TMPBUF_SZ; +const BUF_BYTES : uint = 2048u; + /// Returns the platform-specific value of errno pub fn errno() -> int { #[cfg(any(target_os = "macos", @@ -110,3 +120,122 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { Err(super::last_error()) } } + +pub fn getcwd() -> IoResult { + use c_str::CString; + + let mut buf = [0 as c_char, ..BUF_BYTES]; + unsafe { + if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { + Err(IoError::last_error()) + } else { + Ok(Path::new(CString::new(buf.as_ptr(), false))) + } + } +} + +pub unsafe fn get_env_pairs() -> Vec> { + use c_str::CString; + + extern { + fn rust_env_pairs() -> *const *const c_char; + } + let mut environ = rust_env_pairs(); + if environ as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + let mut result = Vec::new(); + while *environ != 0 as *const _ { + let env_pair = + CString::new(*environ, false).as_bytes_no_nul().to_vec(); + result.push(env_pair); + environ = environ.offset(1); + } + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec { + unparsed.split(|b| *b == b':').map(Path::new).collect() +} + +pub fn join_paths(paths: &[T]) -> Result, &'static str> { + let mut joined = Vec::new(); + let sep = b':'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&sep) { return Err("path segment contains separator `:`") } + joined.push_all(path); + } + + Ok(joined) +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn load_self() -> Option> { + unsafe { + use libc::funcs::bsd44::*; + use libc::consts::os::extra::*; + let mut mib = vec![CTL_KERN as c_int, + KERN_PROC as c_int, + KERN_PROC_PATHNAME as c_int, + -1 as c_int]; + let mut sz: libc::size_t = 0; + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + ptr::null_mut(), &mut sz, ptr::null_mut(), + 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + let mut v: Vec = Vec::with_capacity(sz as uint); + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + v.as_mut_ptr() as *mut c_void, &mut sz, + ptr::null_mut(), 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn load_self() -> Option> { + use std::io; + + match io::fs::readlink(&Path::new("/proc/self/exe")) { + Ok(path) => Some(path.into_vec()), + Err(..) => None + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn load_self() -> Option> { + unsafe { + use libc::funcs::extra::_NSGetExecutablePath; + let mut sz: u32 = 0; + _NSGetExecutablePath(ptr::null_mut(), &mut sz); + if sz == 0 { return None; } + let mut v: Vec = Vec::with_capacity(sz as uint); + let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); + if err != 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + p.with_c_str(|buf| { + unsafe { + match libc::chdir(buf) == (0 as c_int) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } + }) +} + +pub fn page_size() -> uint { + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as uint + } +} diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index aa43b42e74643..5c690180c44d4 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -8,17 +8,27 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of `std::os` functionality for Windows + // FIXME: move various extern bindings from here into liblibc or // something similar -use libc; -use libc::{c_int, c_char, c_void}; use prelude::*; + +use fmt; use io::{IoResult, IoError}; +use libc::{c_int, c_char, c_void}; +use libc; +use os; +use path::{Path, GenericPath, BytesContainer}; +use ptr::{mod, RawPtr}; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use sys::fs::FileDesc; -use ptr; use os::TMPBUF_SZ; +use libc::types::os::arch::extra::DWORD; + +const BUF_BYTES : uint = 2048u; pub fn errno() -> uint { use libc::types::os::arch::extra::DWORD; @@ -101,3 +111,185 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { _ => Err(IoError::last_error()), } } + +pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option { + unsafe { + let mut n = TMPBUF_SZ as DWORD; + let mut res = None; + let mut done = false; + while !done { + let mut buf = Vec::from_elem(n as uint, 0u16); + let k = f(buf.as_mut_ptr(), n); + if k == (0 as DWORD) { + done = true; + } else if k == n && + libc::GetLastError() == + libc::ERROR_INSUFFICIENT_BUFFER as DWORD { + n *= 2 as DWORD; + } else if k >= n { + n = k; + } else { + done = true; + } + if k != 0 && done { + let sub = buf.slice(0, k as uint); + // We want to explicitly catch the case when the + // closure returned invalid UTF-16, rather than + // set `res` to None and continue. + let s = String::from_utf16(sub) + .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); + res = option::Some(s) + } + } + return res; + } +} + +pub fn getcwd() -> IoResult { + use libc::DWORD; + use libc::GetCurrentDirectoryW; + use io::OtherIoError; + + let mut buf = [0 as u16, ..BUF_BYTES]; + unsafe { + if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { + return Err(IoError::last_error()); + } + } + + match String::from_utf16(::str::truncate_utf16_at_nul(&buf)) { + Some(ref cwd) => Ok(Path::new(cwd)), + None => Err(IoError { + kind: OtherIoError, + desc: "GetCurrentDirectoryW returned invalid UTF-16", + detail: None, + }), + } +} + +pub unsafe fn get_env_pairs() -> Vec> { + use libc::funcs::extra::kernel32::{ + GetEnvironmentStringsW, + FreeEnvironmentStringsW + }; + let ch = GetEnvironmentStringsW(); + if ch as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + // Here, we lossily decode the string as UTF16. + // + // The docs suggest that the result should be in Unicode, but + // Windows doesn't guarantee it's actually UTF16 -- it doesn't + // validate the environment string passed to CreateProcess nor + // SetEnvironmentVariable. Yet, it's unlikely that returning a + // raw u16 buffer would be of practical use since the result would + // be inherently platform-dependent and introduce additional + // complexity to this code. + // + // Using the non-Unicode version of GetEnvironmentStrings is even + // worse since the result is in an OEM code page. Characters that + // can't be encoded in the code page would be turned into question + // marks. + let mut result = Vec::new(); + let mut i = 0; + while *ch.offset(i) != 0 { + let p = &*ch.offset(i); + let mut len = 0; + while *(p as *const _).offset(len) != 0 { + len += 1; + } + let p = p as *const u16; + let s = slice::from_raw_buf(&p, len as uint); + result.push(String::from_utf16_lossy(s).into_bytes()); + i += len as int + 1; + } + FreeEnvironmentStringsW(ch); + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec { + // On Windows, the PATH environment variable is semicolon separated. Double + // quotes are used as a way of introducing literal semicolons (since + // c:\some;dir is a valid Windows path). Double quotes are not themselves + // permitted in path names, so there is no way to escape a double quote. + // Quoted regions can appear in arbitrary locations, so + // + // c:\foo;c:\som"e;di"r;c:\bar + // + // Should parse as [c:\foo, c:\some;dir, c:\bar]. + // + // (The above is based on testing; there is no clear reference available + // for the grammar.) + + let mut parsed = Vec::new(); + let mut in_progress = Vec::new(); + let mut in_quote = false; + + for b in unparsed.iter() { + match *b { + b';' if !in_quote => { + parsed.push(Path::new(in_progress.as_slice())); + in_progress.truncate(0) + } + b'"' => { + in_quote = !in_quote; + } + _ => { + in_progress.push(*b); + } + } + } + parsed.push(Path::new(in_progress)); + parsed +} + +pub fn join_paths(paths: &[T]) -> Result, &'static str> { + let mut joined = Vec::new(); + let sep = b';'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&b'"') { + return Err("path segment contains `\"`"); + } else if path.contains(&sep) { + joined.push(b'"'); + joined.push_all(path); + joined.push(b'"'); + } else { + joined.push_all(path); + } + } + + Ok(joined) +} + +pub fn load_self() -> Option> { + unsafe { + fill_utf16_buf_and_decode(|buf, sz| { + libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) + }).map(|s| s.into_string().into_bytes()) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + let mut p = p.as_str().unwrap().utf16_units().collect::>(); + p.push(0); + + unsafe { + match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } +} + +pub fn page_size() -> uint { + use mem; + unsafe { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + + return info.dwPageSize as uint; + } +} From b66681cd31674e1a2d0b9675ef8183c463470bb5 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Nov 2014 16:38:06 -0800 Subject: [PATCH 03/24] Allow args to work without rt initialization --- src/libstd/os.rs | 6 +----- src/libstd/rt/args.rs | 22 +++++++++------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 550e9e06aae01..f46d9ab7c7e4b 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -702,11 +702,7 @@ fn real_args_as_bytes() -> Vec> { target_os = "dragonfly"))] fn real_args_as_bytes() -> Vec> { use rt; - - match rt::args::clone() { - Some(args) => args, - None => panic!("process arguments not initialized") - } + rt::args::clone().unwrap_or_else(|| vec![]) } #[cfg(not(windows))] diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index 8b9dbf73c53b9..93c956fc3c54a 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -62,37 +62,33 @@ mod imp { } pub unsafe fn cleanup() { - rtassert!(take().is_some()); + take(); LOCK.destroy(); } pub fn take() -> Option>> { - with_lock(|| unsafe { + let guard = LOCK.lock(); + unsafe { let ptr = get_global_ptr(); let val = mem::replace(&mut *ptr, None); val.as_ref().map(|s: &Box>>| (**s).clone()) - }) + } } pub fn put(args: Vec>) { - with_lock(|| unsafe { + let guard = LOCK.lock(); + unsafe { let ptr = get_global_ptr(); rtassert!((*ptr).is_none()); (*ptr) = Some(box args.clone()); - }) + } } pub fn clone() -> Option>> { - with_lock(|| unsafe { + let guard = LOCK.lock(); + unsafe { let ptr = get_global_ptr(); (*ptr).as_ref().map(|s: &Box>>| (**s).clone()) - }) - } - - fn with_lock(f: F) -> T where F: FnOnce() -> T { - unsafe { - let _guard = LOCK.lock(); - f() } } From c009bfdf94a48434e1f6bf3bfe59cf539f464ee2 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Nov 2014 17:22:40 -0800 Subject: [PATCH 04/24] Make at_exit initialize lazily --- src/liblibc/lib.rs | 4 ++-- src/libstd/os.rs | 1 - src/libstd/rt/at_exit_imp.rs | 41 ++++++++++++++++++++---------------- src/libstd/rt/mod.rs | 2 -- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 8825099e36c35..6ab00cfe8fa6a 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -138,7 +138,7 @@ pub use funcs::c95::stdio::{fread, freopen, fseek, fsetpos, ftell}; pub use funcs::c95::stdio::{fwrite, perror, puts, remove, rename, rewind}; pub use funcs::c95::stdio::{setbuf, setvbuf, tmpfile, ungetc}; -pub use funcs::c95::stdlib::{abs, atof, atoi, calloc, exit, _exit}; +pub use funcs::c95::stdlib::{abs, atof, atoi, calloc, exit, _exit, atexit}; pub use funcs::c95::stdlib::{free, getenv, labs, malloc, rand}; pub use funcs::c95::stdlib::{realloc, srand, strtod, strtol}; pub use funcs::c95::stdlib::{strtoul, system}; @@ -4102,7 +4102,7 @@ pub mod funcs { pub fn free(p: *mut c_void); pub fn exit(status: c_int) -> !; pub fn _exit(status: c_int) -> !; - // Omitted: atexit. + pub fn atexit(cb: extern fn()) -> c_int; pub fn system(s: *const c_char) -> c_int; pub fn getenv(s: *const c_char) -> *mut c_char; // Omitted: bsearch, qsort diff --git a/src/libstd/os.rs b/src/libstd/os.rs index f46d9ab7c7e4b..550c8d2fabaca 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -43,7 +43,6 @@ use boxed::Box; use ops::{Drop, FnOnce}; use option::Option; use option::Option::{Some, None}; -use os; use path::{Path, GenericPath, BytesContainer}; use sys; use ptr::RawPtr; diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index 086079c312ad4..9ddb59bfffcf5 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -14,9 +14,10 @@ use core::prelude::*; +use libc; use boxed::Box; use vec::Vec; -use sync::atomic; +use sync::{atomic, Once, ONCE_INIT}; use mem; use thunk::Thunk; @@ -24,31 +25,21 @@ use rt::exclusive::Exclusive; type Queue = Exclusive>; +static INIT: Once = ONCE_INIT; static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; static RUNNING: atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL; -pub fn init() { +fn init() { let state: Box = box Exclusive::new(Vec::new()); unsafe { - rtassert!(!RUNNING.load(atomic::SeqCst)); - assert!(QUEUE.swap(mem::transmute(state), atomic::SeqCst) == 0); - } -} - -pub fn push(f: Thunk) { - 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(atomic::SeqCst)); - let queue = QUEUE.load(atomic::SeqCst); - rtassert!(queue != 0); - (*(queue as *const Queue)).lock().push(f); + QUEUE.store(mem::transmute(state), atomic::SeqCst); + libc::atexit(run); } } -pub fn run() { +// Note: this is private and so can only be called via atexit above, +// which guarantees initialization. +extern fn run() { let cur = unsafe { rtassert!(!RUNNING.load(atomic::SeqCst)); let queue = QUEUE.swap(0, atomic::SeqCst); @@ -63,3 +54,17 @@ pub fn run() { to_run.invoke(()); } } + +pub fn push(f: Thunk) { + INIT.doit(init); + 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(atomic::SeqCst)); + let queue = QUEUE.load(atomic::SeqCst); + rtassert!(queue != 0); + (*(queue as *const Queue)).lock().push(f); + } +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 21c8197ef0558..5d5ccefda5dc2 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -100,7 +100,6 @@ pub fn init(argc: int, argv: *const *const u8) { unsafe { args::init(argc, argv); local_ptr::init(); - at_exit_imp::init(); thread::init(); unwind::register(failure::on_fail); } @@ -212,7 +211,6 @@ pub unsafe fn cleanup() { args::cleanup(); thread::cleanup(); local_ptr::cleanup(); - at_exit_imp::run(); } // FIXME: these probably shouldn't be public... From 9b03b72d7fb82f07d35e7dcda02754c6da90ae58 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Nov 2014 17:33:37 -0800 Subject: [PATCH 05/24] Remove rt::bookkeeping This commit removes the runtime bookkeeping previously used to ensure that all Rust tasks were joined before the runtime was shut down. This functionality will be replaced by an RAII style `Thread` API, that will also offer a detached mode. Since this changes the semantics of shutdown, it is a: [breaking-change] --- src/libstd/rt/bookkeeping.rs | 61 -------------------------- src/libstd/rt/mod.rs | 2 - src/libstd/rt/task.rs | 7 --- src/libstd/sys/common/helper_thread.rs | 3 +- 4 files changed, 1 insertion(+), 72 deletions(-) delete mode 100644 src/libstd/rt/bookkeeping.rs diff --git a/src/libstd/rt/bookkeeping.rs b/src/libstd/rt/bookkeeping.rs deleted file mode 100644 index aca520fc088f5..0000000000000 --- a/src/libstd/rt/bookkeeping.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2013-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. - -//! Task bookkeeping -//! -//! This module keeps track of the number of running tasks so that entry points -//! with libnative know when it's possible to exit the program (once all tasks -//! have exited). -//! -//! The green counterpart for this is bookkeeping on sched pools, and it's up to -//! each respective runtime to make sure that they call increment() and -//! decrement() manually. - -use sync::atomic; -use ops::Drop; - -use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - -static TASK_COUNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; -static TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - -#[allow(missing_copy_implementations)] -pub struct Token { _private: () } - -impl Drop for Token { - fn drop(&mut self) { decrement() } -} - -/// Increment the number of live tasks, returning a token which will decrement -/// the count when dropped. -pub fn increment() -> Token { - let _ = TASK_COUNT.fetch_add(1, atomic::SeqCst); - Token { _private: () } -} - -pub fn decrement() { - unsafe { - if TASK_COUNT.fetch_sub(1, atomic::SeqCst) == 1 { - let guard = TASK_LOCK.lock(); - guard.signal(); - } - } -} - -/// Waits for all other native tasks in the system to exit. This is only used by -/// the entry points of native programs -pub fn wait_for_other_tasks() { - unsafe { - let guard = TASK_LOCK.lock(); - while TASK_COUNT.load(atomic::SeqCst) > 0 { - guard.wait(); - } - } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 5d5ccefda5dc2..676dbb0b498a6 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -73,7 +73,6 @@ pub mod mutex; pub mod thread; pub mod exclusive; pub mod util; -pub mod bookkeeping; pub mod local; pub mod task; pub mod unwind; @@ -207,7 +206,6 @@ 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(); args::cleanup(); thread::cleanup(); local_ptr::cleanup(); diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index babd111b3c221..98940a2b381c7 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -29,7 +29,6 @@ use str::SendStr; use thunk::Thunk; use rt; -use rt::bookkeeping; use rt::mutex::NativeMutex; use rt::local::Local; use rt::thread::{mod, Thread}; @@ -132,11 +131,6 @@ impl Task { let stack = stack_size.unwrap_or(rt::min_stack()); - // Note that this increment must happen *before* the spawn in order to - // guarantee that if this task exits it will always end up waiting for - // the spawned task to exit. - let token = bookkeeping::increment(); - // Spawning a new OS thread guarantees that __morestack will never get // triggered, but we must manually set up the actual stack bounds once // this function starts executing. This raises the lower limit by a bit @@ -156,7 +150,6 @@ impl Task { let mut f = Some(f); drop(task.run(|| { f.take().unwrap().invoke(()) }).destroy()); - drop(token); }) } diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index ffb053e852efc..ef0181b72b0b7 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -25,7 +25,7 @@ use prelude::*; use cell::UnsafeCell; use mem; use sync::{StaticMutex, StaticCondvar}; -use rt::{mod, bookkeeping}; +use rt; use sys::helper_signal; use task; @@ -83,7 +83,6 @@ impl Helper { let t = f(); task::spawn(move |:| { - bookkeeping::decrement(); helper(receive, rx, t); let _g = self.lock.lock(); *self.shutdown.get() = true; From cac133c9a86a4687755aeb44908e3fbb2bb35fc2 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Tue, 25 Nov 2014 08:52:10 -0800 Subject: [PATCH 06/24] Introduce std::thread Also removes: * `std::task` * `std::rt::task` * `std::rt::thread` Notes for the new API are in a follow-up commit. Closes #18000 --- src/libstd/lib.rs | 4 +- src/libstd/rt/mod.rs | 35 +- src/libstd/rt/thread.rs | 171 ------- src/libstd/sys/common/thread_info.rs | 60 +++ src/libstd/task.rs | 537 +--------------------- src/libstd/thread.rs | 655 +++++++++++++++++++++++++++ 6 files changed, 742 insertions(+), 720 deletions(-) delete mode 100644 src/libstd/rt/thread.rs create mode 100644 src/libstd/sys/common/thread_info.rs create mode 100644 src/libstd/thread.rs diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d7f331b6c230e..a0939999c7c19 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -227,9 +227,9 @@ pub mod time; pub mod collections; pub mod hash; -/* Tasks and communication */ +/* Threads and communication */ -pub mod task; +pub mod thread; pub mod sync; pub mod comm; diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 676dbb0b498a6..eff80b5ab2f7b 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -53,7 +53,9 @@ use failure; use os; use thunk::Thunk; use kinds::Send; +use thread::Thread; use sys_common; +use sys_common::thread::{mod, NewThread}; // Reexport some of our utilities which are expected by other crates. pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; @@ -73,8 +75,6 @@ pub mod mutex; pub mod thread; pub mod exclusive; pub mod util; -pub mod local; -pub mod task; pub mod unwind; mod args; @@ -98,8 +98,8 @@ pub fn init(argc: int, argv: *const *const u8) { // Need to propagate the unsafety to `start`. unsafe { args::init(argc, argv); - local_ptr::init(); - thread::init(); + sys::thread::guard::init(); + sys::stack_overflow::init(); unwind::register(failure::on_fail); } } @@ -125,9 +125,6 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { /// This procedure is guaranteed to run on the thread calling this function, but /// the stack bounds for this rust task will *not* be set. Care must be taken /// for this function to not overflow its stack. -/// -/// This function will only return once *all* native threads in the system have -/// exited. pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { use prelude::*; use rt; @@ -143,11 +140,9 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { // frames above our current position. let my_stack_bottom = my_stack_top + 20000 - OS_DEFAULT_STACK_ESTIMATE; - // When using libgreen, one of the first things that we do is to turn off - // the SIGPIPE signal (set it to ignore). By default, some platforms will - // send a *signal* when a EPIPE error would otherwise be delivered. This - // runtime doesn't install a SIGPIPE handler, causing it to kill the - // program, which isn't exactly what we want! + // By default, some platforms will send a *signal* when a EPIPE error would + // otherwise be delivered. This runtime doesn't install a SIGPIPE handler, + // causing it to kill the program, which isn't exactly what we want! // // Hence, we set SIGPIPE to ignore when the program starts up in order to // prevent this problem. @@ -163,17 +158,18 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { init(argc, argv); let mut exit_code = None; - let mut main = Some(main); - let mut task = box Task::new(Some((my_stack_bottom, my_stack_top)), - Some(rt::thread::main_guard_page())); - task.name = Some(str::Slice("
")); - drop(task.run(|| { + + let thread: std::Thread = NewThread::new(Some("
".into_string())); + thread_info::set((my_stack_bottom, my_stack_top), + unsafe { sys::thread::guard::main() }, + thread); + unwind::try(|| { unsafe { sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); } (main.take().unwrap()).invoke(()); exit_code = Some(os::get_exit_status()); - }).destroy()); + }); unsafe { cleanup(); } // If the exit code wasn't set, then the task block must have panicked. return exit_code.unwrap_or(rt::DEFAULT_ERROR_CODE); @@ -207,8 +203,7 @@ pub fn at_exit(f: proc():Send) { /// undefined behavior. pub unsafe fn cleanup() { args::cleanup(); - thread::cleanup(); - local_ptr::cleanup(); + sys::stack_overflow::cleanup(); } // FIXME: these probably shouldn't be public... diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs deleted file mode 100644 index c10338b1bceb2..0000000000000 --- a/src/libstd/rt/thread.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2013-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. - -//! Native os-thread management -//! -//! This modules contains bindings necessary for managing OS-level threads. -//! These functions operate outside of the rust runtime, creating threads -//! which are not used for scheduling in any way. - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use boxed::Box; -use mem; -use sys::stack_overflow; -use sys::thread as imp; - -pub unsafe fn init() { - imp::guard::init(); - stack_overflow::init(); -} - -pub unsafe fn cleanup() { - stack_overflow::cleanup(); -} - -/// This struct represents a native thread's state. This is used to join on an -/// existing thread created in the join-able state. -pub struct Thread { - native: imp::rust_thread, - joined: bool, - packet: Box>, -} - -static DEFAULT_STACK_SIZE: uint = 1024 * 1024; - -/// Returns the last writable byte of the main thread's stack next to the guard -/// page. Must be called from the main thread. -pub fn main_guard_page() -> uint { - unsafe { - imp::guard::main() - } -} - -/// Returns the last writable byte of the current thread's stack next to the -/// guard page. Must not be called from the main thread. -pub fn current_guard_page() -> uint { - unsafe { - imp::guard::current() - } -} - -// There are two impl blocks b/c if T were specified at the top then it's just a -// pain to specify a type parameter on Thread::spawn (which doesn't need the -// type parameter). -impl Thread<()> { - - /// Starts execution of a new OS thread. - /// - /// This function will not wait for the thread to join, but a handle to the - /// thread will be returned. - /// - /// Note that the handle returned is used to acquire the return value of the - /// procedure `main`. The `join` function will wait for the thread to finish - /// and return the value that `main` generated. - /// - /// Also note that the `Thread` returned will *always* wait for the thread - /// to finish executing. This means that even if `join` is not explicitly - /// called, when the `Thread` falls out of scope its destructor will block - /// waiting for the OS thread. - pub fn start(main: proc():Send -> T) -> Thread { - Thread::start_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `start`, but specifies an explicit - /// stack size for the new thread. - pub fn start_stack(stack: uint, main: proc():Send -> T) -> Thread { - - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let packet = box None; - let packet2: *mut Option = unsafe { - *mem::transmute::<&Box>, *const *mut Option>(&packet) - }; - let main = proc() unsafe { *packet2 = Some(main()); }; - let native = unsafe { imp::create(stack, box main) }; - - Thread { - native: native, - joined: false, - packet: packet, - } - } - - /// This will spawn a new thread, but it will not wait for the thread to - /// finish, nor is it possible to wait for the thread to finish. - /// - /// This corresponds to creating threads in the 'detached' state on unix - /// systems. Note that platforms may not keep the main program alive even if - /// there are detached thread still running around. - pub fn spawn(main: proc():Send) { - Thread::spawn_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `spawn`, but explicitly specifies a - /// stack size for the new thread. - pub fn spawn_stack(stack: uint, main: proc():Send) { - unsafe { - let handle = imp::create(stack, box main); - imp::detach(handle); - } - } - - /// Relinquishes the CPU slot that this OS-thread is currently using, - /// allowing another thread to run for awhile. - pub fn yield_now() { - unsafe { imp::yield_now(); } - } -} - -impl Thread { - /// Wait for this thread to finish, returning the result of the thread's - /// calculation. - pub fn join(mut self) -> T { - assert!(!self.joined); - unsafe { imp::join(self.native) }; - self.joined = true; - assert!(self.packet.is_some()); - self.packet.take().unwrap() - } -} - -#[unsafe_destructor] -impl Drop for Thread { - fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. - if !self.joined { - unsafe { imp::join(self.native) }; - } - } -} - -#[cfg(test)] -mod tests { - use super::Thread; - - #[test] - fn smoke() { Thread::start(proc (){}).join(); } - - #[test] - fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } - - #[test] - fn detached() { Thread::spawn(proc () {}) } - - #[test] - fn small_stacks() { - assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); - assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); - } -} diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs new file mode 100644 index 0000000000000..fb0231b44ba73 --- /dev/null +++ b/src/libstd/sys/common/thread_info.rs @@ -0,0 +1,60 @@ +// 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. + +struct ThreadInfo { + // This field holds the known bounds of the stack in (lo, hi) + // form. Not all threads necessarily know their precise bounds, + // hence this is optional. + stack_bounds: (uint, uint), + stack_guard: uint, + unwinder: Unwinder, + thread: Thread, +} + +thread_local!(static THREAD_INFO: RefCell> = RefCell::new(None)); + +impl ThreadInfo { + fn with(f: |&ThreadInfo| -> R) -> R { + THREAD_INFO.with(|c| { + if c.borrow().is_none() { + *c.borrow_mut() = Some(ThreadInfo { + stack_bounds: (0, 0), + stack_guard: 0, + unwinder: Unwinder::new(), + thread: Thread::new(None), + }) + } + f(c.borrow().as_ref().unwrap()) + }) + } +} + +pub fn current_thread() -> Thread { + ThreadInfo::with(|info| info.thread.clone()) +} + +pub fn panicking() -> bool { + ThreadInfo::with(|info| info.unwinder.unwinding()) +} + +pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { + THREAD_INFO.with(|c| assert!(c.borrow().is_none())); + THREAD_INFO.with(|c| *c.borrow_mut() = Some(ThreadInfo{ + stack_bounds: stack_bounds, + stack_guard: stack_guard, + unwinder: Unwinder::new(), + thread: thread, + })); +} + +// a hack to get around privacy restrictions; implemented by `std::thread::Thread` +pub trait NewThread { + fn new(name: Option) -> Self; +} diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 127cad186f693..6881a1adb25fb 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -8,536 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Task creation -//! -//! An executing Rust program consists of a collection of tasks, each -//! with their own stack and local state. -//! -//! Tasks generally have their memory *isolated* from each other by -//! virtue of Rust's owned types (which of course may only be owned by -//! a single task at a time). Communication between tasks is primarily -//! done through [channels](../../std/comm/index.html), Rust's -//! message-passing types, though [other forms of task -//! synchronization](../../std/sync/index.html) are often employed to -//! achieve particular performance goals. In particular, types that -//! are guaranteed to be threadsafe are easily shared between threads -//! using the atomically-reference-counted container, -//! [`Arc`](../../std/sync/struct.Arc.html). -//! -//! Fatal logic errors in Rust cause *task panic*, during which -//! a task will unwind the stack, running destructors and freeing -//! owned resources. Task panic is unrecoverable from within -//! the panicking task (i.e. there is no 'try/catch' in Rust), but -//! panic may optionally be detected from a different task. If -//! the main task panics the application will exit with a non-zero -//! exit code. -//! -//! # Examples -//! -//! ```rust -//! spawn(move|| { -//! println!("Hello, World!"); -//! }) -//! ``` +//! Deprecated in favor of `thread`. -#![unstable = "The task spawning model will be changed as part of runtime reform, and the module \ - will likely be renamed from `task` to `thread`."] +#![deprecated = "use std::thread instead"] -use any::Any; -use borrow::IntoCow; -use boxed::Box; -use comm::channel; -use core::ops::FnOnce; -use io::{Writer, stdio}; +use thread; use kinds::Send; -use option::Option; -use option::Option::{None, Some}; -use result::Result; -use rt::local::Local; -use rt::task; -use rt::task::Task; -use str::SendStr; -use string::{String, ToString}; -use thunk::{Thunk}; -use sync::Future; -/// The task builder type. -/// -/// Provides detailed control over the properties and behavior of new tasks. +/// Deprecate: use `std::thread::Cfg` instead. +#[deprecated = "use std::thread::Cfg instead"] +pub type TaskBuilder = thread::Cfg; -// NB: Builders are designed to be single-use because they do stateful -// things that get weird when reusing - e.g. if you create a result future -// it only applies to a single task, so then you have to maintain Some -// potentially tricky state to ensure that everything behaves correctly -// when you try to reuse the builder to spawn a new task. We'll just -// sidestep that whole issue by making builders uncopyable and making -// the run function move them in. -pub struct TaskBuilder { - // A name for the task-to-be, for identification in panic messages - name: Option, - // The size of the stack for the spawned task - stack_size: Option, - // Task-local stdout - stdout: Option>, - // Task-local stderr - stderr: Option>, - // Optionally wrap the eventual task body - gen_body: Option>, -} - -impl TaskBuilder { - /// Generate the base configuration for spawning a task, off of which more - /// configuration methods can be chained. - pub fn new() -> TaskBuilder { - TaskBuilder { - name: None, - stack_size: None, - stdout: None, - stderr: None, - gen_body: None, - } - } -} - -impl TaskBuilder { - /// Name the task-to-be. Currently the name is used for identification - /// only in panic messages. - #[unstable = "IntoMaybeOwned will probably change."] - pub fn named>(mut self, name: T) -> TaskBuilder { - self.name = Some(name.into_cow()); - self - } - - /// Set the size of the stack for the new task. - pub fn stack_size(mut self, size: uint) -> TaskBuilder { - self.stack_size = Some(size); - self - } - - /// Redirect task-local stdout. - #[experimental = "May not want to make stdio overridable here."] - pub fn stdout(mut self, stdout: Box) -> TaskBuilder { - self.stdout = Some(stdout); - self - } - - /// Redirect task-local stderr. - #[experimental = "May not want to make stdio overridable here."] - pub fn stderr(mut self, stderr: Box) -> TaskBuilder { - self.stderr = Some(stderr); - self - } - - // Where spawning actually happens (whether yielding a future or not) - fn spawn_internal( - self, - f: Thunk, - on_exit: Option>) - { - let TaskBuilder { - name, stack_size, stdout, stderr, mut gen_body - } = self; - - let f = match gen_body.take() { - Some(gen) => gen.invoke(f), - None => f - }; - - let opts = task::TaskOpts { - on_exit: on_exit, - name: name, - stack_size: stack_size, - }; - if stdout.is_some() || stderr.is_some() { - Task::spawn(opts, move|:| { - let _ = stdout.map(stdio::set_stdout); - let _ = stderr.map(stdio::set_stderr); - f.invoke(()); - }); - } else { - Task::spawn(opts, move|:| f.invoke(())) - } - } - - /// Creates and executes a new child task. - /// - /// Sets up a new task with its own call stack and schedules it to run - /// the provided function. The task has the properties and behavior - /// specified by the `TaskBuilder`. - pub fn spawn(self, f: F) { - self.spawn_internal(Thunk::new(f), None) - } - - /// Execute a function in a newly-spawned task and return a future representing - /// the task's result. The task has the properties and behavior - /// specified by the `TaskBuilder`. - /// - /// Taking the value of the future will block until the child task - /// terminates. - /// - /// # Return value - /// - /// If the child task executes successfully (without panicking) then the - /// future returns `result::Result::Ok` containing the value returned by the - /// function. If the child task panics then the future returns - /// `result::Result::Err` containing the argument to `panic!(...)` as an - /// `Any` trait object. - #[experimental = "Futures are experimental."] - pub fn try_future(T)+Send>(self, f: F) - -> Future>> { - // currently, the on_exit fn provided by librustrt only works for unit - // results, so we use an additional side-channel to communicate the - // result. - - let (tx_done, rx_done) = channel(); // signal that task has exited - let (tx_retv, rx_retv) = channel(); // return value from task - - let on_exit: Thunk = Thunk::with_arg(move |: res: task::Result| { - let _ = tx_done.send_opt(res); - }); - self.spawn_internal(Thunk::new(move |:| { let _ = tx_retv.send_opt(f()); }), - Some(on_exit)); - - Future::from_fn(move|:| { - rx_done.recv().map(|_| rx_retv.recv()) - }) - } - - /// Execute a function in a newly-spawnedtask and block until the task - /// completes or panics. Equivalent to `.try_future(f).unwrap()`. - #[unstable = "Error type may change."] - pub fn try(self, f: F) -> Result> - where F : FnOnce() -> T, F : Send, T : Send - { - self.try_future(f).into_inner() - } -} - -/* Convenience functions */ - -/// Creates and executes a new child task -/// -/// Sets up a new task with its own call stack and schedules it to run -/// the provided unique closure. -/// -/// This function is equivalent to `TaskBuilder::new().spawn(f)`. -pub fn spawn(f: F) { - TaskBuilder::new().spawn(f) -} - -/// Execute a function in a newly-spawned task and return either the return -/// value of the function or an error if the task panicked. -/// -/// This is equivalent to `TaskBuilder::new().try`. -#[unstable = "Error type may change."] -pub fn try(f: F) -> Result> - where T : Send, F : FnOnce() -> T, F : Send -{ - TaskBuilder::new().try(f) -} - -/// Execute a function in another task and return a future representing the -/// task's result. -/// -/// This is equivalent to `TaskBuilder::new().try_future`. -#[experimental = "Futures are experimental."] -pub fn try_future(f: F) -> Future>> - where T:Send, F:FnOnce()->T, F:Send -{ - TaskBuilder::new().try_future(f) -} - -/* Lifecycle functions */ - -/// Read the name of the current task. -#[stable] -pub fn name() -> Option { - use rt::task::Task; - - let task = Local::borrow(None::); - match task.name { - Some(ref name) => Some(name.to_string()), - None => None - } -} - -/// Yield control to the task scheduler. -#[unstable = "Name will change."] -pub fn deschedule() { - use rt::task::Task; - Task::yield_now(); -} - -/// True if the running task is currently panicking (e.g. will return `true` inside a -/// destructor that is run while unwinding the stack after a call to `panic!()`). -#[unstable = "May move to a different module."] -pub fn failing() -> bool { - use rt::task::Task; - Local::borrow(None::).unwinder.unwinding() -} - -#[cfg(test)] -mod test { - use any::{Any, AnyRefExt}; - use borrow::IntoCow; - use boxed::BoxAny; - use prelude::*; - use result::Result::{Ok, Err}; - use result; - use std::io::{ChanReader, ChanWriter}; - use string::String; - use thunk::Thunk; - use prelude::*; - use super::*; - - // !!! These tests are dangerous. If something is buggy, they will hang, !!! - // !!! instead of exiting cleanly. This might wedge the buildbots. !!! - - #[test] - fn test_unnamed_task() { - try(move|| { - assert!(name().is_none()); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_owned_named_task() { - TaskBuilder::new().named("ada lovelace".to_string()).try(move|| { - assert!(name().unwrap() == "ada lovelace"); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_static_named_task() { - TaskBuilder::new().named("ada lovelace").try(move|| { - assert!(name().unwrap() == "ada lovelace"); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_send_named_task() { - TaskBuilder::new().named("ada lovelace".into_cow()).try(move|| { - assert!(name().unwrap() == "ada lovelace"); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_run_basic() { - let (tx, rx) = channel(); - TaskBuilder::new().spawn(move|| { - tx.send(()); - }); - rx.recv(); - } - - #[test] - fn test_try_future() { - let result = TaskBuilder::new().try_future(move|| {}); - assert!(result.unwrap().is_ok()); - - let result = TaskBuilder::new().try_future(move|| -> () { - panic!(); - }); - assert!(result.unwrap().is_err()); - } - - #[test] - fn test_try_success() { - match try(move|| { - "Success!".to_string() - }).as_ref().map(|s| s.as_slice()) { - result::Result::Ok("Success!") => (), - _ => panic!() - } - } - - #[test] - fn test_try_panic() { - match try(move|| { - panic!() - }) { - result::Result::Err(_) => (), - result::Result::Ok(()) => panic!() - } - } - - #[test] - fn test_spawn_sched() { - use clone::Clone; - - let (tx, rx) = channel(); - - fn f(i: int, tx: Sender<()>) { - let tx = tx.clone(); - spawn(move|| { - if i == 0 { - tx.send(()); - } else { - f(i - 1, tx); - } - }); - - } - f(10, tx); - rx.recv(); - } - - #[test] - fn test_spawn_sched_childs_on_default_sched() { - let (tx, rx) = channel(); - - spawn(move|| { - spawn(move|| { - tx.send(()); - }); - }); - - rx.recv(); - } - - fn avoid_copying_the_body(spawnfn: F) where - F: FnOnce(Thunk), - { - let (tx, rx) = channel::(); - - let x = box 1; - let x_in_parent = (&*x) as *const int as uint; - - spawnfn(Thunk::new(move|| { - let x_in_child = (&*x) as *const int as uint; - tx.send(x_in_child); - })); - - let x_in_child = rx.recv(); - assert_eq!(x_in_parent, x_in_child); - } - - #[test] - fn test_avoid_copying_the_body_spawn() { - avoid_copying_the_body(|t| spawn(move|| t.invoke(()))); - } - - #[test] - fn test_avoid_copying_the_body_task_spawn() { - avoid_copying_the_body(|f| { - let builder = TaskBuilder::new(); - builder.spawn(move|| f.invoke(())); - }) - } - - #[test] - fn test_avoid_copying_the_body_try() { - avoid_copying_the_body(|f| { - let _ = try(move|| f.invoke(())); - }) - } - - #[test] - fn test_child_doesnt_ref_parent() { - // If the child refcounts the parent task, this will stack overflow when - // climbing the task tree to dereference each ancestor. (See #1789) - // (well, it would if the constant were 8000+ - I lowered it to be more - // valgrind-friendly. try this at home, instead..!) - static GENERATIONS: uint = 16; - fn child_no(x: uint) -> Thunk { - return Thunk::new(move|| { - if x < GENERATIONS { - TaskBuilder::new().spawn(move|| child_no(x+1).invoke(())); - } - }); - } - TaskBuilder::new().spawn(|| child_no(0).invoke(())); - } - - #[test] - fn test_simple_newsched_spawn() { - spawn(move|| ()) - } - - #[test] - fn test_try_panic_message_static_str() { - match try(move|| { - panic!("static string"); - }) { - Err(e) => { - type T = &'static str; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "static string"); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_owned_str() { - match try(move|| { - panic!("owned string".to_string()); - }) { - Err(e) => { - type T = String; - assert!(e.is::()); - assert_eq!(*e.downcast::().unwrap(), "owned string"); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_any() { - match try(move|| { - panic!(box 413u16 as Box); - }) { - Err(e) => { - type T = Box; - assert!(e.is::()); - let any = e.downcast::().unwrap(); - assert!(any.is::()); - assert_eq!(*any.downcast::().unwrap(), 413u16); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_unit_struct() { - struct Juju; - - match try(move|| { - panic!(Juju) - }) { - Err(ref e) if e.is::() => {} - Err(_) | Ok(()) => panic!() - } - } - - #[test] - fn test_stdout() { - let (tx, rx) = channel(); - let mut reader = ChanReader::new(rx); - let stdout = ChanWriter::new(tx); - - let r = TaskBuilder::new().stdout(box stdout as Box) - .try(move|| { - print!("Hello, world!"); - }); - assert!(r.is_ok()); - - let output = reader.read_to_string().unwrap(); - assert_eq!(output, "Hello, world!"); - } - - // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due - // to the test harness apparently interfering with stderr configuration. -} - -#[test] -fn task_abort_no_kill_runtime() { - use std::io::timer; - use time::Duration; - use mem; - - let tb = TaskBuilder::new(); - let rx = tb.try_future(move|| {}); - mem::drop(rx); - timer::sleep(Duration::milliseconds(1000)); +/// Deprecated: use `std::thread::Thread::spawn` instead. +#[deprecated = "use std::thread::Thread::spawn instead"] +pub fn spawn(f: proc(): Send) { + thread::Thread::spawn(f); } diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs new file mode 100644 index 0000000000000..a6e114bc2c356 --- /dev/null +++ b/src/libstd/thread.rs @@ -0,0 +1,655 @@ +// 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. + +//! Native threads +//! +//! ## The threading model +//! +//! An executing Rust program consists of a collection of native OS threads, +//! each with their own stack and local state. +//! +//! Threads generally have their memory *isolated* from each other by virtue of +//! Rust's owned types (which of course may only be owned by a single thread at +//! a time). Communication between threads can be done through +//! [channels](../../std/comm/index.html), Rust's message-passing types, along +//! with [other forms of thread synchronization](../../std/sync/index.html) and +//! shared-memory data structures. In particular, types that are guaranteed to +//! be threadsafe are easily shared between threads using the +//! atomically-reference-counted container, +//! [`Arc`](../../std/sync/struct.Arc.html). +//! +//! Fatal logic errors in Rust cause *thread panic*, during which +//! a thread will unwind the stack, running destructors and freeing +//! owned resources. Thread panic is unrecoverable from within +//! the panicking thread (i.e. there is no 'try/catch' in Rust), but +//! panic may optionally be detected from a different thread. If +//! the main thread panics the application will exit with a non-zero +//! exit code. +//! +//! When the main thread of a Rust program terminates, the entire program shuts +//! down, even if other threads are still running. However, this module provides +//! convenient facilities for automatically waiting for the termination of a +//! child thread (i.e., join), described below. +//! +//! ## The `Thread` type +//! +//! Already-running threads are represented via the `Thread` type, which you can +//! get in one of two ways: +//! +//! * By spawning a new thread, e.g. using the `Thread::spawn` constructor; +//! * By requesting the current thread, using the `Thread::current` function. +//! +//! Threads can be named, and provide some built-in support for low-level +//! synchronization described below. +//! +//! The `Thread::current()` function is available even for threads not spawned +//! by the APIs of this module. +//! +//! ## Spawning a thread +//! +//! There are a few different ways to spawn a new thread, depending on how it +//! should relate to the parent thread. +//! +//! ### Simple detached threads +//! +//! The simplest case just spawns a completely independent (detached) thread, +//! returning a new `Thread` handle to it: +//! +//! ```rust +//! use std::thread::Thread; +//! +//! Thread::spawn(proc() { +//! println!("Hello, World!"); +//! }) +//! ``` +//! +//! The spawned thread may outlive its parent. +//! +//! ### Joining +//! +//! Alternatively, the `with_join` constructor spawns a new thread and returns a +//! `JoinGuard` which can be used to wait until the child thread completes, +//! returning its result (or `Err` if the child thread panicked): +//! +//! ```rust +//! use std::thread::Thread; +//! +//! let guard = Thread::with_join(proc() { panic!() }; +//! assert!(guard.join().is_err()); +//! ``` +//! +//! The guard works in RAII style, meaning that the child thread is +//! automatically joined when the guard is dropped. A handle to the thread +//! itself is available via the `thread` method on the guard. +//! +//! ### Configured threads +//! +//! Finally, a new thread can be configured independently of how it is +//! spawned. Configuration is available via the `Cfg` builder, which currently +//! allows you to set the name, stack size, and writers for `println!` and +//! `panic!` for the child thread: +//! +//! ```rust +//! use std::thread; +//! +//! thread::cfg().name("child1").spawn(proc() { println!("Hello, world!") }); +//! ``` +//! +//! ## Blocking support: park and unpark +//! +//! Every thread is equipped with some basic low-level blocking support, via the +//! `park` and `unpark` functions. +//! +//! Conceptually, each `Thread` handle has an associated token, which is +//! initially not present: +//! +//! * The `Thread::park()` function blocks the current thread unless or until +//! the token is available for its thread handle, at which point It atomically +//! consumes the token. It may also return *spuriously*, without consuming the +//! token. +//! +//! * The `unpark()` method on a `Thread` atomically makes the token available +//! if it wasn't already. +//! +//! In other words, each `Thread` acts a bit like a semaphore with initial count +//! 0, except that the semaphore is *saturating* (the count cannot go above 1), +//! and can return spuriously. +//! +//! The API is typically used by acquiring a handle to the current thread, +//! placing that handle in a shared data structure so that other threads can +//! find it, and then `park`ing. When some desired condition is met, another +//! thread calls `unpark` on the handle. +//! +//! The motivation for this design is twofold: +//! +//! * It avoids the need to allocate mutexes and condvars when building new +//! synchronization primitives; the threads already provide basic blocking/signaling. +//! +//! * It can be implemented highly efficiently on many platforms. + +use core::prelude::*; + +use any::Any; +use borrow::IntoCow; +use boxed::Box; +use mem; +use sync::{Mutex, Condvar, Arc}; +use string::String; +use rt::{mod, unwind}; +use io::{Writer, stdio}; + +use sys::thread as imp; +use sys_common::{stack, thread_info}; + +/// Thread configuation. Provides detailed control over the properties +/// and behavior of new threads. +pub struct Cfg { + // A name for the thread-to-be, for identification in panic messages + name: Option, + // The size of the stack for the spawned thread + stack_size: Option, + // Thread-local stdout + stdout: Option>, + // Thread-local stderr + stderr: Option>, +} + +impl Cfg { + /// Generate the base configuration for spawning a thread, from which + /// configuration methods can be chained. + pub fn new() -> Cfg { + Cfg { + name: None, + stack_size: None, + stdout: None, + stderr: None, + } + } + + /// Name the thread-to-be. Currently the name is used for identification + /// only in panic messages. + pub fn name(mut self, name: String) -> Cfg { + self.name = Some(name); + self + } + + /// Deprecated: use `name` instead + #[deprecated = "use name instead"] + pub fn named>(self, name: T) -> Cfg { + self.name(name.into_cow().into_owned()) + } + + /// Set the size of the stack for the new thread. + pub fn stack_size(mut self, size: uint) -> Cfg { + self.stack_size = Some(size); + self + } + + /// Redirect thread-local stdout. + #[experimental = "Will likely go away after proc removal"] + pub fn stdout(mut self, stdout: Box) -> Cfg { + self.stdout = Some(stdout); + self + } + + /// Redirect thread-local stderr. + #[experimental = "Will likely go away after proc removal"] + pub fn stderr(mut self, stderr: Box) -> Cfg { + self.stderr = Some(stderr); + self + } + + fn core_spawn(self, f: proc():Send -> T, after: proc(Result):Send) + -> (imp::rust_thread, Thread) + { + let Cfg { name, stack_size, stdout, stderr } = self; + + let stack_size = stack_size.unwrap_or(rt::min_stack()); + let my_thread = Thread::new(name); + let their_thread = my_thread.clone(); + + // Spawning a new OS thread guarantees that __morestack will never get + // triggered, but we must manually set up the actual stack bounds once + // this function starts executing. This raises the lower limit by a bit + // because by the time that this function is executing we've already + // consumed at least a little bit of stack (we don't know the exact byte + // address at which our stack started). + let main = proc() { + let something_around_the_top_of_the_stack = 1; + let addr = &something_around_the_top_of_the_stack as *const int; + let my_stack_top = addr as uint; + let my_stack_bottom = my_stack_top - stack_size + 1024; + unsafe { + stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); + } + thread_info::set( + (my_stack_bottom, my_stack_top), + thread::current_guard_page(), + their_thread + ); + + // There are two primary reasons that general try/catch is + // unsafe. The first is that we do not support nested try/catch. The + // fact that this is happening in a newly-spawned thread + // suffices. The second is that unwinding while unwinding is not + // defined. We take care of that by having an 'unwinding' flag in + // the thread itself. For these reasons, this unsafety should be ok. + unsafe { + let mut output = None; + let mut f_opt = Some( // option dance + if stdout.is_some() || stderr.is_some() { + proc() { + let _ = stdout.map(stdio::set_stdout); + let _ = stderr.map(stdio::set_stderr); + f() + } + } else { + f + }); + let try_result = unwind::try(|| output = Some((f_opt.take().unwrap())())); + match (output, try_result) { + (Some(data), Ok(_)) => after(Ok(data)), + (None, Err(cause)) => after(Err(cause)), + _ => unreachable!() + } + } + }; + (unsafe { imp::create(stack, box main) }, my_thread) + } + + /// Spawn a detached thread, and return a handle to it. + /// + /// The new child thread may outlive its parent. + pub fn spawn(self, f: proc():Send) -> Thread { + let (native, thread) = self.core_spawn(f, proc(_) {}); + unsafe { imp::detach(native) }; + thread + } + + /// Spawn a joinable thread, and return an RAII guard for it. + pub fn with_join(self, f: proc():Send -> T) -> JoinGuard { + // We need the address of the packet to fill in to be stable so when + // `main` fills it in it's still valid, so allocate an extra box to do + // so. + let my_packet = box Err(box 0); // sentinel value + let their_packet: *mut Result = unsafe { + *mem::transmute::<&Box>, *const *mut Result>(&my_packet) + }; + + let (native, thread) = self.core_spawn(f, proc(result) { + *their_packet = result; + }); + + JoinGuard { + native: native, + joined: false, + packet: my_packet, + thread: thread, + } + } +} + +/// A convenience function for creating configurations. +pub fn cfg() -> Cfg { Cfg::new() } + +struct Inner { + name: Option, + lock: Mutex, // true when there is a buffered unpark + cvar: Condvar, +} + +#[deriving(Clone)] +/// A handle to a thread. +pub struct Thread { + inner: Arc, +} + +impl Thread { + fn new(name: Option) -> Thread { + Thread { + inner: Arc::new(Inner { + name: name, + lock: Mutex::new(false), + cvar: Condvar::new(), + }) + } + } + + /// Spawn a detached thread, and return a handle to it. + /// + /// The new child thread may outlive its parent. + pub fn spawn(f: proc():Send) -> Thread { + Cfg::new().spawn(f) + } + + /// Spawn a joinable thread, and return an RAII guard for it. + pub fn with_join(f: proc():Send -> T) -> JoinGuard { + Cfg::new().with_join(f) + } + + /// Gets a handle to the thread that invokes it. + pub fn current() -> Thread { + ThreadInfo::current_thread() + } + + /// Cooperatively give up a timeslice to the OS scheduler. + pub fn yield_now() { + unsafe { imp::yield_now() } + } + + /// Determines whether the current thread is panicking. + pub fn panicking() -> bool { + ThreadInfo::panicking() + } + + // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp + /// Block unless or until the current thread's token is made available (may wake spuriously). + /// + /// See the module doc for more detail. + pub fn park() { + let thread = Thread::current(); + let guard = thread.inner.lock.lock(); + while !*guard { + thread.inner.cvar.wait(guard); + } + *guard = false; + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// See the module doc for more detail. + pub fn unpark(&self) { + let guard = self.inner.lock(); + if !*guard { + *guard = true; + self.inner.cvar.notify_one(); + } + } + + /// Get the thread's name. + pub fn name(&self) -> Option<&str> { + self.inner.name.as_ref() + } +} + +// a hack to get around privacy restrictions +impl thread_info::NewThread for Thread { + fn new(name: Option) -> Thread { Thread::new(name) } +} + +/// Indicates the manner in which a thread exited. +/// +/// A thread that completes without panicking is considered to exit successfully. +pub type Result = result::Result>; + +#[must_use] +/// An RAII guard that will block until thread termination when dropped. +pub struct JoinGuard { + native: imp::rust_thread, + thread: Thread, + joined: bool, + packet: Box>, +} + +impl JoinGuard { + /// Extract a handle to the thread this guard will join on. + pub fn thread(&self) -> Thread { + self.thread.clone() + } + + /// Wait for the associated thread to finish, returning the result of the thread's + /// calculation. + pub fn join(mut self) -> Result { + assert!(!self.joined); + unsafe { imp::join(self.native) }; + self.joined = true; + let box res = self.packet.take().unwrap(); + res + } +} + +#[unsafe_destructor] +impl Drop for JoinGuard { + fn drop(&mut self) { + // This is required for correctness. If this is not done then the thread + // would fill in a return box which no longer exists. + if !self.joined { + unsafe { imp::join(self.native) }; + } + } +} + +// TODO: fix tests +#[cfg(test)] +mod test { + use any::{Any, AnyRefExt}; + use boxed::BoxAny; + use prelude::*; + use result::Result::{Ok, Err}; + use result; + use std::io::{ChanReader, ChanWriter}; + use string::String; + use super::{Thread, cfg}; + + // !!! These tests are dangerous. If something is buggy, they will hang, !!! + // !!! instead of exiting cleanly. This might wedge the buildbots. !!! + + #[test] + fn test_unnamed_thread() { + Thread::with_join(proc() { + assert!(Thread::current().name().is_none()); + }).join().map_err(|_| ()).unwrap(); + } + + #[test] + fn test_named_thread() { + cfg().name("ada lovelace".to_string()).with_join(proc() { + assert!(Thread::current().name().unwrap() == "ada lovelace".to_string()); + }).join().map_err(|_| ()).unwrap(); + } + + #[test] + fn test_run_basic() { + let (tx, rx) = channel(); + Thread::spawn(proc() { + tx.send(()); + }); + rx.recv(); + } + + #[test] + fn test_join_success() { + match Thread::with_join::(proc() { + "Success!".to_string() + }).join().as_ref().map(|s| s.as_slice()) { + result::Result::Ok("Success!") => (), + _ => panic!() + } + } + + #[test] + fn test_join_panic() { + match Thread::with_join(proc() { + panic!() + }).join() { + result::Result::Err(_) => (), + result::Result::Ok(()) => panic!() + } + } + + #[test] + fn test_spawn_sched() { + use clone::Clone; + + let (tx, rx) = channel(); + + fn f(i: int, tx: Sender<()>) { + let tx = tx.clone(); + Thread::spawn(proc() { + if i == 0 { + tx.send(()); + } else { + f(i - 1, tx); + } + }); + + } + f(10, tx); + rx.recv(); + } + + #[test] + fn test_spawn_sched_childs_on_default_sched() { + let (tx, rx) = channel(); + + Thread::spawn(proc() { + Thread::spawn(proc() { + tx.send(()); + }); + }); + + rx.recv(); + } + + fn avoid_copying_the_body(spawnfn: |v: proc():Send|) { + let (tx, rx) = channel::(); + + let x = box 1; + let x_in_parent = (&*x) as *const int as uint; + + spawnfn(proc() { + let x_in_child = (&*x) as *const int as uint; + tx.send(x_in_child); + }); + + let x_in_child = rx.recv(); + assert_eq!(x_in_parent, x_in_child); + } + + #[test] + fn test_avoid_copying_the_body_spawn() { + avoid_copying_the_body(|v| { Thread::spawn(v); }); + } + + #[test] + fn test_avoid_copying_the_body_thread_spawn() { + avoid_copying_the_body(|f| { + let builder = cfg(); + builder.spawn(proc() { + f(); + }); + }) + } + + #[test] + fn test_avoid_copying_the_body_join() { + avoid_copying_the_body(|f| { + let _ = Thread::with_join(proc() { + f() + }).join(); + }) + } + + #[test] + fn test_child_doesnt_ref_parent() { + // If the child refcounts the parent task, this will stack overflow when + // climbing the task tree to dereference each ancestor. (See #1789) + // (well, it would if the constant were 8000+ - I lowered it to be more + // valgrind-friendly. try this at home, instead..!) + static GENERATIONS: uint = 16; + fn child_no(x: uint) -> proc(): Send { + return proc() { + if x < GENERATIONS { + Thread::spawn(child_no(x+1)); + } + } + } + Thread::spawn(child_no(0)); + } + + #[test] + fn test_simple_newsched_spawn() { + Thread::spawn(proc()()); + } + + #[test] + fn test_try_panic_message_static_str() { + match Thread::with_join(proc() { + panic!("static string"); + }).join() { + Err(e) => { + type T = &'static str; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "static string"); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_owned_str() { + match Thread::with_join(proc() { + panic!("owned string".to_string()); + }).join() { + Err(e) => { + type T = String; + assert!(e.is::()); + assert_eq!(*e.downcast::().unwrap(), "owned string".to_string()); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_any() { + match Thread::with_join(proc() { + panic!(box 413u16 as Box); + }).join() { + Err(e) => { + type T = Box; + assert!(e.is::()); + let any = e.downcast::().unwrap(); + assert!(any.is::()); + assert_eq!(*any.downcast::().unwrap(), 413u16); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_unit_struct() { + struct Juju; + + match Thread::with_join(proc() { + panic!(Juju) + }).join() { + Err(ref e) if e.is::() => {} + Err(_) | Ok(()) => panic!() + } + } + + #[test] + fn test_stdout() { + let (tx, rx) = channel(); + let mut reader = ChanReader::new(rx); + let stdout = ChanWriter::new(tx); + + let r = cfg().stdout(box stdout as Box).with_join(proc() { + print!("Hello, world!"); + }).join(); + assert!(r.is_ok()); + + let output = reader.read_to_string().unwrap(); + assert_eq!(output, "Hello, world!".to_string()); + } + + // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due + // to the test harness apparently interfering with stderr configuration. +} From 84cb6cd9386ab01ba59f8ed98d698f9af74e65fe Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 24 Nov 2014 17:59:15 -0800 Subject: [PATCH 07/24] Remove rt::{local, local_data, thread_local_storage} --- src/libstd/rt/local_ptr.rs | 404 -------------------------- src/libstd/rt/mod.rs | 11 +- src/libstd/rt/thread.rs | 170 +++++++++++ src/libstd/rt/thread_local_storage.rs | 115 -------- 4 files changed, 176 insertions(+), 524 deletions(-) delete mode 100644 src/libstd/rt/local_ptr.rs create mode 100644 src/libstd/rt/thread.rs delete mode 100644 src/libstd/rt/thread_local_storage.rs diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs deleted file mode 100644 index a87bc3d27662e..0000000000000 --- a/src/libstd/rt/local_ptr.rs +++ /dev/null @@ -1,404 +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. - -//! Access to a single thread-local pointer. -//! -//! The runtime will use this for storing Box. -//! -//! FIXME: Add runtime checks for usage of inconsistent pointer types. -//! and for overwriting an existing pointer. - -#![allow(dead_code)] - -use core::prelude::*; - -use mem; -use boxed::Box; - -#[cfg(any(windows, // mingw-w32 doesn't like thread_local things - target_os = "android", // see #10686 - target_os = "ios"))] -pub use self::native::{init, cleanup, put, take, try_take, unsafe_take, exists, - unsafe_borrow, try_unsafe_borrow}; - -#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] -pub use self::compiled::{init, cleanup, put, take, try_take, unsafe_take, exists, - unsafe_borrow, try_unsafe_borrow}; - -/// Encapsulates a borrowed value. When this value goes out of scope, the -/// pointer is returned. -pub struct Borrowed { - val: *const (), -} - -#[unsafe_destructor] -impl Drop for Borrowed { - fn drop(&mut self) { - unsafe { - if self.val.is_null() { - rtabort!("Aiee, returning null borrowed object!"); - } - let val: Box = mem::transmute(self.val); - put::(val); - rtassert!(exists()); - } - } -} - -impl Deref for Borrowed { - fn deref<'a>(&'a self) -> &'a T { - unsafe { &*(self.val as *const T) } - } -} - -impl DerefMut for Borrowed { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { - unsafe { &mut *(self.val as *mut T) } - } -} - -/// Borrow the thread-local value from thread-local storage. -/// While the value is borrowed it is not available in TLS. -/// -/// # Safety note -/// -/// Does not validate the pointer type. -#[inline] -pub unsafe fn borrow() -> Borrowed { - let val: *const () = mem::transmute(take::()); - Borrowed { - val: val, - } -} - -/// Compiled implementation of accessing the runtime local pointer. This is -/// implemented using LLVM's thread_local attribute which isn't necessarily -/// working on all platforms. This implementation is faster, however, so we use -/// it wherever possible. -#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] -pub mod compiled { - use core::prelude::*; - - use boxed::Box; - use mem; - - #[cfg(test)] - pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; - - #[cfg(not(test))] - #[thread_local] - pub static mut RT_TLS_PTR: *mut u8 = 0 as *mut u8; - - pub fn init() {} - - pub unsafe fn cleanup() {} - - // Rationale for all of these functions being inline(never) - // - // The #[thread_local] annotation gets propagated all the way through to - // LLVM, meaning the global is specially treated by LLVM to lower it to an - // efficient sequence of instructions. This also involves dealing with fun - // stuff in object files and whatnot. Regardless, it turns out this causes - // trouble with green threads and lots of optimizations turned on. The - // following case study was done on Linux x86_64, but I would imagine that - // other platforms are similar. - // - // On Linux, the instruction sequence for loading the tls pointer global - // looks like: - // - // mov %fs:0x0, %rax - // mov -0x8(%rax), %rbx - // - // This code leads me to believe that (%fs:0x0) is a table, and then the - // table contains the TLS values for the process. Hence, the slot at offset - // -0x8 is the task TLS pointer. This leads us to the conclusion that this - // table is the actual thread local part of each thread. The kernel sets up - // the fs segment selector to point at the right region of memory for each - // thread. - // - // Optimizations lead me to believe that this code is lowered to these - // instructions in the LLVM codegen passes, because you'll see code like - // this when everything is optimized: - // - // mov %fs:0x0, %r14 - // mov -0x8(%r14), %rbx - // // do something with %rbx, the rust Task pointer - // - // ... // <- do more things - // - // mov -0x8(%r14), %rbx - // // do something else with %rbx - // - // Note that the optimization done here is that the first load is not - // duplicated during the lower instructions. This means that the %fs:0x0 - // memory location is only dereferenced once. - // - // Normally, this is actually a good thing! With green threads, however, - // it's very possible for the code labeled "do more things" to context - // switch to another thread. If this happens, then we *must* re-load %fs:0x0 - // because it's changed (we're on a different thread). If we don't re-load - // the table location, then we'll be reading the original thread's TLS - // values, not our thread's TLS values. - // - // Hence, we never inline these functions. By never inlining, we're - // guaranteed that loading the table is a local decision which is forced to - // *always* happen. - - /// Give a pointer to thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn put(sched: Box) { - RT_TLS_PTR = mem::transmute(sched) - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn take() -> Box { - let ptr = RT_TLS_PTR; - rtassert!(!ptr.is_null()); - let ptr: Box = mem::transmute(ptr); - // can't use `as`, due to type not matching with `cfg(test)` - RT_TLS_PTR = mem::transmute(0u); - ptr - } - - /// Optionally take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline(never)] // see comments above - pub unsafe fn try_take() -> Option> { - let ptr = RT_TLS_PTR; - if ptr.is_null() { - None - } else { - let ptr: Box = mem::transmute(ptr); - // can't use `as`, due to type not matching with `cfg(test)` - RT_TLS_PTR = mem::transmute(0u); - Some(ptr) - } - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - /// Leaves the old pointer in TLS for speed. - #[inline(never)] // see comments above - pub unsafe fn unsafe_take() -> Box { - mem::transmute(RT_TLS_PTR) - } - - /// Check whether there is a thread-local pointer installed. - #[inline(never)] // see comments above - pub fn exists() -> bool { - unsafe { - RT_TLS_PTR.is_not_null() - } - } - - #[inline(never)] // see comments above - pub unsafe fn unsafe_borrow() -> *mut T { - if RT_TLS_PTR.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - RT_TLS_PTR as *mut T - } - - #[inline(never)] // see comments above - pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { - if RT_TLS_PTR.is_null() { - None - } else { - Some(RT_TLS_PTR as *mut T) - } - } -} - -/// Native implementation of having the runtime thread-local pointer. This -/// implementation uses the `thread_local_storage` module to provide a -/// thread-local value. -pub mod native { - use core::prelude::*; - - use boxed::Box; - use mem; - use ptr; - use rt::thread_local_storage as tls; - - static mut RT_TLS_KEY: tls::Key = -1; - - /// Initialize the TLS key. Other ops will fail if this isn't executed - /// first. - pub fn init() { - unsafe { - tls::create(&mut RT_TLS_KEY); - } - } - - pub unsafe fn cleanup() { - rtassert!(RT_TLS_KEY != -1); - tls::destroy(RT_TLS_KEY); - } - - /// Give a pointer to thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn put(sched: Box) { - let key = tls_key(); - let void_ptr: *mut u8 = mem::transmute(sched); - tls::set(key, void_ptr); - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn take() -> Box { - let key = tls_key(); - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: Box = mem::transmute(void_ptr); - tls::set(key, ptr::null_mut()); - return ptr; - } - - /// Optionally take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - #[inline] - pub unsafe fn try_take() -> Option> { - match maybe_tls_key() { - Some(key) => { - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - None - } else { - let ptr: Box = mem::transmute(void_ptr); - tls::set(key, ptr::null_mut()); - Some(ptr) - } - } - None => None - } - } - - /// Take ownership of a pointer from thread-local storage. - /// - /// # Safety note - /// - /// Does not validate the pointer type. - /// Leaves the old pointer in TLS for speed. - #[inline] - pub unsafe fn unsafe_take() -> Box { - let key = tls_key(); - let void_ptr: *mut u8 = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - let ptr: Box = mem::transmute(void_ptr); - return ptr; - } - - /// Check whether there is a thread-local pointer installed. - pub fn exists() -> bool { - unsafe { - match maybe_tls_key() { - Some(key) => tls::get(key).is_not_null(), - None => false - } - } - } - - /// Borrow a mutable reference to the thread-local value - /// - /// # Safety Note - /// - /// Because this leaves the value in thread-local storage it is possible - /// For the Scheduler pointer to be aliased - pub unsafe fn unsafe_borrow() -> *mut T { - let key = tls_key(); - let void_ptr = tls::get(key); - if void_ptr.is_null() { - rtabort!("thread-local pointer is null. bogus!"); - } - void_ptr as *mut T - } - - pub unsafe fn try_unsafe_borrow() -> Option<*mut T> { - match maybe_tls_key() { - Some(key) => { - let void_ptr = tls::get(key); - if void_ptr.is_null() { - None - } else { - Some(void_ptr as *mut T) - } - } - None => None - } - } - - #[inline] - fn tls_key() -> tls::Key { - match maybe_tls_key() { - Some(key) => key, - None => rtabort!("runtime tls key not initialized") - } - } - - #[inline] - #[cfg(not(test))] - pub fn maybe_tls_key() -> Option { - unsafe { - // NB: This is a little racy because, while the key is - // initialized under a mutex and it's assumed to be initialized - // in the Scheduler ctor by any thread that needs to use it, - // we are not accessing the key under a mutex. Threads that - // are not using the new Scheduler but still *want to check* - // whether they are running under a new Scheduler may see a 0 - // value here that is in the process of being initialized in - // another thread. I think this is fine since the only action - // they could take if it was initialized would be to check the - // thread-local value and see that it's not set. - if RT_TLS_KEY != -1 { - return Some(RT_TLS_KEY); - } else { - return None; - } - } - } - - #[inline] #[cfg(test)] - pub fn maybe_tls_key() -> Option { - use rt; - unsafe { - mem::transmute(::realstd::rt::shouldnt_be_public::maybe_tls_key()) - } - } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index eff80b5ab2f7b..8ef7ac43a30ef 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -75,13 +75,15 @@ pub mod mutex; pub mod thread; pub mod exclusive; pub mod util; +<<<<<<< HEAD +======= +pub mod task; +>>>>>>> Remove rt::{local, local_data, thread_local_storage} pub mod unwind; mod args; mod at_exit_imp; mod libunwind; -mod local_ptr; -mod thread_local_storage; /// The default error code of the rust runtime if the main task panics instead /// of exiting cleanly. @@ -98,8 +100,7 @@ pub fn init(argc: int, argv: *const *const u8) { // Need to propagate the unsafety to `start`. unsafe { args::init(argc, argv); - sys::thread::guard::init(); - sys::stack_overflow::init(); + thread::init(); unwind::register(failure::on_fail); } } @@ -203,7 +204,7 @@ pub fn at_exit(f: proc():Send) { /// undefined behavior. pub unsafe fn cleanup() { args::cleanup(); - sys::stack_overflow::cleanup(); + thread::cleanup(); } // FIXME: these probably shouldn't be public... diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs new file mode 100644 index 0000000000000..9eb7048f1e699 --- /dev/null +++ b/src/libstd/rt/thread.rs @@ -0,0 +1,170 @@ +// Copyright 2013-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. + +//! Native os-thread management +//! +//! This modules contains bindings necessary for managing OS-level threads. +//! These functions operate outside of the rust runtime, creating threads +//! which are not used for scheduling in any way. + +#![allow(non_camel_case_types)] + +use core::prelude::*; + +use boxed::Box; +use mem; +use sys::stack_overflow; +use sys::thread as imp; + +pub unsafe fn init() { + imp::guard::init(); + stack_overflow::init(); +} + +pub unsafe fn cleanup() { + stack_overflow::cleanup(); +} + +/// This struct represents a native thread's state. This is used to join on an +/// existing thread created in the join-able state. +pub struct Thread { + native: imp::rust_thread, + joined: bool, + packet: Box>, +} + +static DEFAULT_STACK_SIZE: uint = 1024 * 1024; + +/// Returns the last writable byte of the main thread's stack next to the guard +/// page. Must be called from the main thread. +pub fn main_guard_page() -> uint { + unsafe { + imp::guard::main() + } +} + +/// Returns the last writable byte of the current thread's stack next to the +/// guard page. Must not be called from the main thread. +pub fn current_guard_page() -> uint { + unsafe { + imp::guard::current() + } +} + +// There are two impl blocks b/c if T were specified at the top then it's just a +// pain to specify a type parameter on Thread::spawn (which doesn't need the +// type parameter). +impl Thread<()> { + /// Starts execution of a new OS thread. + /// + /// This function will not wait for the thread to join, but a handle to the + /// thread will be returned. + /// + /// Note that the handle returned is used to acquire the return value of the + /// procedure `main`. The `join` function will wait for the thread to finish + /// and return the value that `main` generated. + /// + /// Also note that the `Thread` returned will *always* wait for the thread + /// to finish executing. This means that even if `join` is not explicitly + /// called, when the `Thread` falls out of scope its destructor will block + /// waiting for the OS thread. + pub fn start(main: proc():Send -> T) -> Thread { + Thread::start_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `start`, but specifies an explicit + /// stack size for the new thread. + pub fn start_stack(stack: uint, main: proc():Send -> T) -> Thread { + + // We need the address of the packet to fill in to be stable so when + // `main` fills it in it's still valid, so allocate an extra box to do + // so. + let packet = box None; + let packet2: *mut Option = unsafe { + *mem::transmute::<&Box>, *const *mut Option>(&packet) + }; + let main = proc() unsafe { *packet2 = Some(main()); }; + let native = unsafe { imp::create(stack, box main) }; + + Thread { + native: native, + joined: false, + packet: packet, + } + } + + /// This will spawn a new thread, but it will not wait for the thread to + /// finish, nor is it possible to wait for the thread to finish. + /// + /// This corresponds to creating threads in the 'detached' state on unix + /// systems. Note that platforms may not keep the main program alive even if + /// there are detached thread still running around. + pub fn spawn(main: proc():Send) { + Thread::spawn_stack(DEFAULT_STACK_SIZE, main) + } + + /// Performs the same functionality as `spawn`, but explicitly specifies a + /// stack size for the new thread. + pub fn spawn_stack(stack: uint, main: proc():Send) { + unsafe { + let handle = imp::create(stack, box main); + imp::detach(handle); + } + } + + /// Relinquishes the CPU slot that this OS-thread is currently using, + /// allowing another thread to run for awhile. + pub fn yield_now() { + unsafe { imp::yield_now(); } + } +} + +impl Thread { + /// Wait for this thread to finish, returning the result of the thread's + /// calculation. + pub fn join(mut self) -> T { + assert!(!self.joined); + unsafe { imp::join(self.native) }; + self.joined = true; + assert!(self.packet.is_some()); + self.packet.take().unwrap() + } +} + +#[unsafe_destructor] +impl Drop for Thread { + fn drop(&mut self) { + // This is required for correctness. If this is not done then the thread + // would fill in a return box which no longer exists. + if !self.joined { + unsafe { imp::join(self.native) }; + } + } +} + +#[cfg(test)] +mod tests { + use super::Thread; + + #[test] + fn smoke() { Thread::start(proc (){}).join(); } + + #[test] + fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } + + #[test] + fn detached() { Thread::spawn(proc () {}) } + + #[test] + fn small_stacks() { + assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); + assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); + } +} diff --git a/src/libstd/rt/thread_local_storage.rs b/src/libstd/rt/thread_local_storage.rs deleted file mode 100644 index ee6ad8a4e08e8..0000000000000 --- a/src/libstd/rt/thread_local_storage.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2013-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. - -#![allow(dead_code)] - -#[cfg(unix)] use libc::c_int; -#[cfg(unix)] use 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!(pthread_key_create(key, null()) == 0); -} - -#[cfg(unix)] -pub unsafe fn set(key: Key, value: *mut u8) { - assert!(pthread_setspecific(key, value) == 0); -} - -#[cfg(unix)] -pub unsafe fn get(key: Key) -> *mut u8 { - pthread_getspecific(key) -} - -#[cfg(unix)] -pub unsafe fn destroy(key: Key) { - assert!(pthread_key_delete(key) == 0); -} - -#[cfg(target_os = "macos")] -#[allow(non_camel_case_types)] // foreign type -type pthread_key_t = ::libc::c_ulong; - -#[cfg(any(target_os="linux", - target_os="freebsd", - target_os="dragonfly", - target_os="android", - target_os = "ios"))] -#[allow(non_camel_case_types)] // foreign type -type pthread_key_t = ::libc::c_uint; - -#[cfg(unix)] -extern { - fn pthread_key_create(key: *mut pthread_key_t, dtor: *const u8) -> c_int; - fn pthread_key_delete(key: pthread_key_t) -> c_int; - fn pthread_getspecific(key: pthread_key_t) -> *mut u8; - fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int; -} - -#[cfg(windows)] -pub type Key = DWORD; - -#[cfg(windows)] -pub unsafe fn create(key: &mut Key) { - static TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; - *key = TlsAlloc(); - assert!(*key != TLS_OUT_OF_INDEXES); -} - -#[cfg(windows)] -pub unsafe fn set(key: Key, value: *mut u8) { - assert!(0 != TlsSetValue(key, value as *mut ::libc::c_void)) -} - -#[cfg(windows)] -pub unsafe fn get(key: Key) -> *mut u8 { - TlsGetValue(key) as *mut u8 -} - -#[cfg(windows)] -pub unsafe fn destroy(key: Key) { - assert!(TlsFree(key) != 0); -} - -#[cfg(windows)] -#[allow(non_snake_case)] -extern "system" { - fn TlsAlloc() -> DWORD; - fn TlsFree(dwTlsIndex: DWORD) -> BOOL; - fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; - fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - - #[test] - fn tls_smoke_test() { - use mem::transmute; - unsafe { - let mut key = 0; - let value = box 20i; - create(&mut key); - set(key, transmute(value)); - let value: Box = transmute(get(key)); - assert_eq!(value, box 20i); - let value = box 30i; - set(key, transmute(value)); - let value: Box = transmute(get(key)); - assert_eq!(value, box 30i); - } - } -} From 7fd7ce682dd6f98d456d817a297b15bdc9841190 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 1 Dec 2014 08:14:35 -0800 Subject: [PATCH 08/24] Add blocking support module for channels --- src/libstd/comm/blocking.rs | 81 ++++++++++++++++ src/libstd/comm/mod.rs | 37 +++----- src/libstd/comm/oneshot.rs | 101 ++++++++++---------- src/libstd/comm/select.rs | 69 ++++++++------ src/libstd/comm/shared.rs | 142 ++++++++++++++-------------- src/libstd/comm/stream.rs | 57 ++++++------ src/libstd/comm/sync.rs | 178 ++++++++++++++++-------------------- 7 files changed, 357 insertions(+), 308 deletions(-) create mode 100644 src/libstd/comm/blocking.rs diff --git a/src/libstd/comm/blocking.rs b/src/libstd/comm/blocking.rs new file mode 100644 index 0000000000000..5e9a01d0151d0 --- /dev/null +++ b/src/libstd/comm/blocking.rs @@ -0,0 +1,81 @@ +// 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. + +//! Generic support for building blocking abstractions. + +use thread::Thread; +use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Ordering}; +use sync::Arc; +use kinds::marker::NoSend; +use mem; +use clone::Clone; + +struct Inner { + thread: Thread, + woken: AtomicBool, +} + +#[deriving(Clone)] +pub struct SignalToken { + inner: Arc, +} + +pub struct WaitToken { + inner: Arc, + no_send: NoSend, +} + +fn token() -> (WaitToken, SignalToken) { + let inner = Arc::new(Inner { + thread: Thread::current(), + woken: INIT_ATOMIC_BOOL, + }); + let wait_token = WaitToken { + inner: inner.clone(), + no_send: NoSend, + }; + let signal_token = SignalToken { + inner: inner + }; + (wait_token, signal_token) +} + +impl SignalToken { + fn signal(&self) -> bool { + let wake = !self.inner.woken.compare_and_swap(false, true, Ordering::SeqCst); + if wake { + self.inner.thread.unpark(); + } + wake + } + + /// Convert to an unsafe uint value. Useful for storing in a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_to_uint(self) -> uint { + mem::transmute(self.inner) + } + + /// Convert from an unsafe uint value. Useful for retrieving a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_from_uint(signal_ptr: uint) -> SignalToken { + SignalToken { inner: mem::transmute(signal_ptr) } + } + +} + +impl WaitToken { + fn wait(self) { + while !self.inner.woken.load(Ordering::SeqCst) { + Thread::park() + } + } +} diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index dfbb09d26b5b4..e5ec0078c5ef0 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -54,17 +54,6 @@ //! There are methods on both of senders and receivers to perform their //! respective operations without panicking, however. //! -//! ## Runtime Requirements -//! -//! The channel types defined in this module generally have very few runtime -//! requirements in order to operate. The major requirement they have is for a -//! local rust `Task` to be available if any *blocking* operation is performed. -//! -//! If a local `Task` is not available (for example an FFI callback), then the -//! `send` operation is safe on a `Sender` (as well as a `send_opt`) as well as -//! the `try_send` method on a `SyncSender`, but no other operations are -//! guaranteed to be safe. -//! //! # Example //! //! Simple usage: @@ -327,9 +316,9 @@ use alloc::arc::Arc; use core::kinds::marker; use core::mem; use core::cell::UnsafeCell; -use rt::task::BlockedTask; pub use comm::select::{Select, Handle}; +use comm::select::StartResult::*; macro_rules! test { { fn $name:ident() $b:block $(#[$a:meta])*} => ( @@ -348,6 +337,7 @@ macro_rules! test { ) } +mod blocking; mod oneshot; mod select; mod shared; @@ -947,34 +937,33 @@ impl select::Packet for Receiver { } } - fn start_selection(&self, mut task: BlockedTask) -> Result<(), BlockedTask>{ + fn start_selection(&self, mut token: SignalToken) -> bool { loop { let (t, new_port) = match *unsafe { self.inner() } { Oneshot(ref p) => { - match unsafe { (*p.get()).start_selection(task) } { - oneshot::SelSuccess => return Ok(()), - oneshot::SelCanceled(task) => return Err(task), + match unsafe { (*p.get()).start_selection(token) } { + oneshot::SelSuccess => return Installed, + oneshot::SelCanceled => return Abort, oneshot::SelUpgraded(t, rx) => (t, rx), } } Stream(ref p) => { - match unsafe { (*p.get()).start_selection(task) } { - stream::SelSuccess => return Ok(()), - stream::SelCanceled(task) => return Err(task), + match unsafe { (*p.get()).start_selection(token) } { + stream::SelSuccess => return Installed, + stream::SelCanceled => return Abort, stream::SelUpgraded(t, rx) => (t, rx), } } Shared(ref p) => { - return unsafe { (*p.get()).start_selection(task) }; + return unsafe { (*p.get()).start_selection(token) }; } Sync(ref p) => { - return unsafe { (*p.get()).start_selection(task) }; + return unsafe { (*p.get()).start_selection(token) }; } }; - task = t; + token = t; unsafe { - mem::swap(self.inner_mut(), - new_port.inner_mut()); + mem::swap(self.inner_mut(), new_port.inner_mut()); } } } diff --git a/src/libstd/comm/oneshot.rs b/src/libstd/comm/oneshot.rs index 2c5248c0897b2..68f3f229cb487 100644 --- a/src/libstd/comm/oneshot.rs +++ b/src/libstd/comm/oneshot.rs @@ -40,17 +40,20 @@ use self::MyUpgrade::*; use core::prelude::*; use alloc::boxed::Box; +use comm::Receiver; +use comm::blocking::{mod, WaitToken, SignalToken}; use core::mem; -use rt::local::Local; -use rt::task::{Task, BlockedTask}; - use sync::atomic; -use comm::Receiver; // Various states you can find a port in. -const EMPTY: uint = 0; -const DATA: uint = 1; -const DISCONNECTED: uint = 2; +const EMPTY: uint = 0; // initial state: no data, no blocked reciever +const DATA: uint = 1; // data ready for receiver to take +const DISCONNECTED: uint = 2; // channel is disconnected OR upgraded +// Any other value represents a pointer to a SignalToken value. The +// protocol ensures that when the state moves *to* a pointer, +// ownership of the token is given to the packet, and when the state +// moves *from* a pointer, ownership of the token is transferred to +// whoever changed the state. pub struct Packet { // Internal state of the chan/port pair (stores the blocked task as well) @@ -71,12 +74,12 @@ pub enum Failure { pub enum UpgradeResult { UpSuccess, UpDisconnected, - UpWoke(BlockedTask), + UpWoke(SignalToken), } pub enum SelectionResult { - SelCanceled(BlockedTask), - SelUpgraded(BlockedTask, Receiver), + SelCanceled, + SelUpgraded(SignalToken, Receiver), SelSuccess, } @@ -118,12 +121,10 @@ impl Packet { // Not possible, these are one-use channels DATA => unreachable!(), - // Anything else means that there was a task waiting on the other - // end. We leave the 'DATA' state inside so it'll pick it up on the - // other end. - n => unsafe { - let t = BlockedTask::cast_from_uint(n); - t.wake().map(|t| t.reawaken()); + // There is a thread waiting on the other end. We leave the 'DATA' + // state inside so it'll pick it up on the other end. + ptr => unsafe { + SignalToken::cast_from_uint(ptr).signal(); Ok(()) } } @@ -142,23 +143,17 @@ impl Packet { // Attempt to not block the task (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(atomic::SeqCst) == EMPTY { - let t: Box = Local::take(); - t.deschedule(1, |task| { - let n = unsafe { task.cast_to_uint() }; - match self.state.compare_and_swap(EMPTY, n, atomic::SeqCst) { - // Nothing on the channel, we legitimately block - EMPTY => Ok(()), - - // If there's data or it's a disconnected channel, then we - // failed the cmpxchg, so we just wake ourselves back up - DATA | DISCONNECTED => { - unsafe { Err(BlockedTask::cast_from_uint(n)) } - } - - // Only one thread is allowed to sleep on this port - _ => unreachable!() - } - }); + let (wait_token, signal_token) = blocking::token(); + let ptr = unsafe { signal_token.cast_to_uint() }; + + // race with senders to enter the blocking state + if self.state.compare_and_swap(EMPTY, ptr, atomic::SeqCst) == EMPTY { + wait_token.wait(); + debug_assert!(self.state.load(atomic::SeqCst) != EMPTY); + } else { + // drop the signal token, since we never blocked + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + } } self.try_recv() @@ -197,6 +192,9 @@ impl Packet { } } } + + // We are the sole receiver; there cannot be a blocking + // receiver already. _ => unreachable!() } } @@ -223,7 +221,7 @@ impl Packet { DISCONNECTED => { self.upgrade = prev; UpDisconnected } // If someone's waiting, we gotta wake them up - n => UpWoke(unsafe { BlockedTask::cast_from_uint(n) }) + ptr => UpWoke(unsafe { SignalToken::cast_from_uint(ptr) }) } } @@ -232,9 +230,8 @@ impl Packet { DATA | DISCONNECTED | EMPTY => {} // If someone's waiting, we gotta wake them up - n => unsafe { - let t = BlockedTask::cast_from_uint(n); - t.wake().map(|t| t.reawaken()); + ptr => unsafe { + SignalToken::cast_from_uint(ptr).signal(); } } } @@ -286,13 +283,17 @@ impl Packet { // Attempts to start selection on this port. This can either succeed, fail // because there is data, or fail because there is an upgrade pending. - pub fn start_selection(&mut self, task: BlockedTask) -> SelectionResult { - let n = unsafe { task.cast_to_uint() }; - match self.state.compare_and_swap(EMPTY, n, atomic::SeqCst) { + pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult { + let ptr = unsafe { token.cast_to_uint() }; + match self.state.compare_and_swap(EMPTY, ptr, atomic::SeqCst) { EMPTY => SelSuccess, - DATA => SelCanceled(unsafe { BlockedTask::cast_from_uint(n) }), + DATA => { + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled + } DISCONNECTED if self.data.is_some() => { - SelCanceled(unsafe { BlockedTask::cast_from_uint(n) }) + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled } DISCONNECTED => { match mem::replace(&mut self.upgrade, SendUsed) { @@ -300,8 +301,7 @@ impl Packet { // propagate upwards whether the upgrade can receive // data GoUp(upgrade) => { - SelUpgraded(unsafe { BlockedTask::cast_from_uint(n) }, - upgrade) + SelUpgraded(unsafe { SignalToken::cast_from_uint(ptr) }, upgrade) } // If the other end disconnected without sending an @@ -309,7 +309,8 @@ impl Packet { // disconnected). up => { self.upgrade = up; - SelCanceled(unsafe { BlockedTask::cast_from_uint(n) }) + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled } } } @@ -331,7 +332,7 @@ impl Packet { // If we've got a blocked task, then use an atomic to gain ownership // of it (may fail) - n => self.state.compare_and_swap(n, EMPTY, atomic::SeqCst) + BLOCKED => self.state.compare_and_swap(BLOCKED, EMPTY, atomic::SeqCst) }; // Now that we've got ownership of our state, figure out what to do @@ -358,11 +359,9 @@ impl Packet { } } - // We woke ourselves up from select. Assert that the task should be - // trashed and returned that we don't have any data. - n => { - let t = unsafe { BlockedTask::cast_from_uint(n) }; - t.trash(); + // We woke ourselves up from select. + ptr => unsafe { + drop(SignalToken::cast_from_uint(ptr)); Ok(false) } } diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index 4da9b4cfa369f..536d38c6e5508 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -59,10 +59,11 @@ use core::cell::Cell; use core::kinds::marker; use core::mem; use core::uint; -use rt::local::Local; -use rt::task::{Task, BlockedTask}; use comm::Receiver; +use comm::blocking::{mod, SignalToken}; + +use self::StartResult::*; /// The "receiver set" of the select interface. This structure is used to manage /// a set of receivers which are being selected over. @@ -93,10 +94,17 @@ pub struct Handle<'rx, T:'rx> { struct Packets { cur: *mut Handle<'static, ()> } +#[doc(hidden)] +#[deriving(PartialEq)] +pub enum StartResult { + Installed, + Abort, +} + #[doc(hidden)] pub trait Packet { fn can_recv(&self) -> bool; - fn start_selection(&self, task: BlockedTask) -> Result<(), BlockedTask>; + fn start_selection(&self, token: SignalToken) -> StartResult; fn abort_selection(&self) -> bool; } @@ -165,36 +173,39 @@ impl Select { // Most notably, the iterations over all of the receivers shouldn't be // necessary. unsafe { - let mut amt = 0; - for p in self.iter() { - amt += 1; - if do_preflight_checks && (*p).packet.can_recv() { - return (*p).id; + // Stage 1: preflight checks. Look for any packets ready to receive + if do_preflight_checks { + for handle in self.iter() { + if (*handle).packet.can_recv() { + return (*handle).id(); + } } } - assert!(amt > 0); - let mut ready_index = amt; - let mut ready_id = uint::MAX; - let mut iter = self.iter().enumerate(); - - // Acquire a number of blocking contexts, and block on each one - // sequentially until one fails. If one fails, then abort - // immediately so we can go unblock on all the other receivers. - let task: Box = Local::take(); - task.deschedule(amt, |task| { - // Prepare for the block - let (i, handle) = iter.next().unwrap(); - match (*handle).packet.start_selection(task) { - Ok(()) => Ok(()), - Err(task) => { - ready_index = i; - ready_id = (*handle).id; - Err(task) + // Stage 2: begin the blocking process + // + // Create a number of signal tokens, and install each one + // sequentially until one fails. If one fails, then abort the + // selection on the already-installed tokens. + let (wait_token, signal_token) = blocking::tokens(); + for (i, handle) in self.iter().enumerate() { + match (*handle).packet.start_selection(signal_token.clone()) { + Installed => {} + Abort => { + // Go back and abort the already-begun selections + for handle in self.iter().take(i) { + (*handle).packet.abort_selection(); + } + return (*handle).id; } } - }); + } + + // Stage 3: no messages available, actually block + wait_token.wait(); + // Stage 4: there *must* be message available; find it. + // // Abort the selection process on each receiver. If the abort // process returns `true`, then that means that the receiver is // ready to receive some data. Note that this also means that the @@ -216,12 +227,14 @@ impl Select { // A rewrite should focus on avoiding a yield loop, and for now this // implementation is tying us over to a more efficient "don't // iterate over everything every time" implementation. - for handle in self.iter().take(ready_index) { + let mut ready_id = uint::MAX; + for handle in self.iter() { if (*handle).packet.abort_selection() { ready_id = (*handle).id; } } + // We must have found a ready receiver assert!(ready_id != uint::MAX); return ready_id; } diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index b3856e588e233..1f1ea2ca9a1b2 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -25,12 +25,12 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rt::local::Local; -use rt::task::{Task, BlockedTask}; -use rt::thread::Thread; use sync::{atomic, Mutex, MutexGuard}; use comm::mpsc_queue as mpsc; +use comm::blocking::{mod, SignalToken}; +use comm::select::StartResult; +use comm::select::StartResult::*; const DISCONNECTED: int = int::MIN; const FUDGE: int = 1024; @@ -43,7 +43,7 @@ pub struct Packet { queue: mpsc::Queue, cnt: atomic::AtomicInt, // How many items are on this channel steals: int, // How many times has a port received without blocking? - to_wake: atomic::AtomicUint, // Task to wake up + to_wake: atomic::AtomicUint, // SignalToken for wake up // The number of channels which are currently using this packet. channels: atomic::AtomicInt, @@ -95,41 +95,34 @@ impl Packet { // // This can only be called at channel-creation time pub fn inherit_blocker(&mut self, - task: Option, + token: Option, guard: MutexGuard<()>) { - match task { - Some(task) => { - assert_eq!(self.cnt.load(atomic::SeqCst), 0); - assert_eq!(self.to_wake.load(atomic::SeqCst), 0); - self.to_wake.store(unsafe { task.cast_to_uint() }, - atomic::SeqCst); - self.cnt.store(-1, atomic::SeqCst); - - // This store is a little sketchy. What's happening here is - // that we're transferring a blocker from a oneshot or stream - // channel to this shared channel. In doing so, we never - // spuriously wake them up and rather only wake them up at the - // appropriate time. This implementation of shared channels - // assumes that any blocking recv() will undo the increment of - // steals performed in try_recv() once the recv is complete. - // This thread that we're inheriting, however, is not in the - // middle of recv. Hence, the first time we wake them up, - // they're going to wake up from their old port, move on to the - // upgraded port, and then call the block recv() function. - // - // When calling this function, they'll find there's data - // immediately available, counting it as a steal. This in fact - // wasn't a steal because we appropriately blocked them waiting - // for data. - // - // To offset this bad increment, we initially set the steal - // count to -1. You'll find some special code in - // abort_selection() as well to ensure that this -1 steal count - // doesn't escape too far. - self.steals = -1; - } - None => {} - } + token.map(|token| { + assert_eq!(self.cnt.load(atomic::SeqCst), 0); + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + self.to_wake.store(unsafe { token.cast_to_uint() }, atomic::SeqCst); + self.cnt.store(-1, atomic::SeqCst); + + // This store is a little sketchy. What's happening here is that + // we're transferring a blocker from a oneshot or stream channel to + // this shared channel. In doing so, we never spuriously wake them + // up and rather only wake them up at the appropriate time. This + // implementation of shared channels assumes that any blocking + // recv() will undo the increment of steals performed in try_recv() + // once the recv is complete. This thread that we're inheriting, + // however, is not in the middle of recv. Hence, the first time we + // wake them up, they're going to wake up from their old port, move + // on to the upgraded port, and then call the block recv() function. + // + // When calling this function, they'll find there's data immediately + // available, counting it as a steal. This in fact wasn't a steal + // because we appropriately blocked them waiting for data. + // + // To offset this bad increment, we initially set the steal count to + // -1. You'll find some special code in abort_selection() as well to + // ensure that this -1 steal count doesn't escape too far. + self.steals = -1; + }); // When the shared packet is constructed, we grabbed this lock. The // purpose of this lock is to ensure that abort_selection() doesn't @@ -175,7 +168,7 @@ impl Packet { self.queue.push(t); match self.cnt.fetch_add(1, atomic::SeqCst) { -1 => { - self.take_to_wake().wake().map(|t| t.reawaken()); + self.take_to_wake().signal(); } // In this case, we have possibly failed to send our data, and @@ -232,10 +225,10 @@ impl Packet { data => return data, } - let task: Box = Local::take(); - task.deschedule(1, |task| { - self.decrement(task) - }); + let (wait_token, signal_token) = blocking::tokens(); + if self.decrement(signal_token) == Installed { + wait_token.wait() + } match self.try_recv() { data @ Ok(..) => { self.steals -= 1; data } @@ -244,10 +237,11 @@ impl Packet { } // Essentially the exact same thing as the stream decrement function. - fn decrement(&mut self, task: BlockedTask) -> Result<(), BlockedTask> { + // Returns true if blocking should proceed. + fn decrement(&mut self, token: SignalToken) -> StartResult { assert_eq!(self.to_wake.load(atomic::SeqCst), 0); - let n = unsafe { task.cast_to_uint() }; - self.to_wake.store(n, atomic::SeqCst); + let ptr = unsafe { token.cast_to_uint() }; + self.to_wake.store(ptr, atomic::SeqCst); let steals = self.steals; self.steals = 0; @@ -258,12 +252,13 @@ impl Packet { // data, we successfully sleep n => { assert!(n >= 0); - if n - steals <= 0 { return Ok(()) } + if n - steals <= 0 { return Installed } } } self.to_wake.store(0, atomic::SeqCst); - Err(unsafe { BlockedTask::cast_from_uint(n) }) + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + Abort } pub fn try_recv(&mut self) -> Result { @@ -271,20 +266,19 @@ impl Packet { mpsc::Data(t) => Some(t), mpsc::Empty => None, - // This is a bit of an interesting case. The channel is - // reported as having data available, but our pop() has - // failed due to the queue being in an inconsistent state. - // This means that there is some pusher somewhere which has - // yet to complete, but we are guaranteed that a pop will - // eventually succeed. In this case, we spin in a yield loop - // because the remote sender should finish their enqueue + // This is a bit of an interesting case. The channel is reported as + // having data available, but our pop() has failed due to the queue + // being in an inconsistent state. This means that there is some + // pusher somewhere which has yet to complete, but we are guaranteed + // that a pop will eventually succeed. In this case, we spin in a + // yield loop because the remote sender should finish their enqueue // operation "very quickly". // // Avoiding this yield loop would require a different queue - // abstraction which provides the guarantee that after M - // pushes have succeeded, at least M pops will succeed. The - // current queues guarantee that if there are N active - // pushes, you can pop N times once all N have finished. + // abstraction which provides the guarantee that after M pushes have + // succeeded, at least M pops will succeed. The current queues + // guarantee that if there are N active pushes, you can pop N times + // once all N have finished. mpsc::Inconsistent => { let data; loop { @@ -354,7 +348,7 @@ impl Packet { } match self.cnt.swap(DISCONNECTED, atomic::SeqCst) { - -1 => { self.take_to_wake().wake().map(|t| t.reawaken()); } + -1 => { self.take_to_wake().signal(); } DISCONNECTED => {} n => { assert!(n >= 0); } } @@ -366,8 +360,7 @@ impl Packet { self.port_dropped.store(true, atomic::SeqCst); let mut steals = self.steals; while { - let cnt = self.cnt.compare_and_swap( - steals, DISCONNECTED, atomic::SeqCst); + let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, atomic::SeqCst); cnt != DISCONNECTED && cnt != steals } { // See the discussion in 'try_recv' for why we yield @@ -382,11 +375,11 @@ impl Packet { } // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&mut self) -> BlockedTask { - let task = self.to_wake.load(atomic::SeqCst); + fn take_to_wake(&mut self) -> SignalToken { + let ptr = self.to_wake.load(atomic::SeqCst); self.to_wake.store(0, atomic::SeqCst); - assert!(task != 0); - unsafe { BlockedTask::cast_from_uint(task) } + assert!(ptr != 0); + unsafe { SignalToken::cast_from_uint(ptr) } } //////////////////////////////////////////////////////////////////////////// @@ -414,19 +407,18 @@ impl Packet { } } - // Inserts the blocked task for selection on this port, returning it back if - // the port already has data on it. + // Inserts the signal token for selection on this port, returning true if + // blocking should proceed. // // The code here is the same as in stream.rs, except that it doesn't need to // peek at the channel to see if an upgrade is pending. - pub fn start_selection(&mut self, - task: BlockedTask) -> Result<(), BlockedTask> { - match self.decrement(task) { - Ok(()) => Ok(()), - Err(task) => { + pub fn start_selection(&mut self, token: SignalToken) -> StartResult { + match self.decrement(token) { + Installed => Installed, + Abort => { let prev = self.bump(1); assert!(prev == DISCONNECTED || prev >= 0); - return Err(task); + Abort } } } @@ -464,7 +456,7 @@ impl Packet { let cur = prev + steals + 1; assert!(cur >= 0); if prev < 0 { - self.take_to_wake().trash(); + drop(self.take_to_wake()); } else { while self.to_wake.load(atomic::SeqCst) != 0 { Thread::yield_now(); diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs index 827b1d51ac48d..a15366d5ebc0c 100644 --- a/src/libstd/comm/stream.rs +++ b/src/libstd/comm/stream.rs @@ -27,13 +27,12 @@ use core::prelude::*; use alloc::boxed::Box; use core::cmp; use core::int; -use rt::local::Local; -use rt::task::{Task, BlockedTask}; -use rt::thread::Thread; +use thread::Thread; use sync::atomic; use comm::spsc_queue as spsc; use comm::Receiver; +use comm::blocking::{mod, WaitToken, SignalToken}; const DISCONNECTED: int = int::MIN; #[cfg(test)] @@ -46,7 +45,7 @@ pub struct Packet { cnt: atomic::AtomicInt, // How many items are on this channel steals: int, // How many times has a port received without blocking? - to_wake: atomic::AtomicUint, // Task to wake up + to_wake: atomic::AtomicUint, // SignalToken for the blocked thread to wake up port_dropped: atomic::AtomicBool, // flag if the channel has been destroyed. } @@ -60,13 +59,13 @@ pub enum Failure { pub enum UpgradeResult { UpSuccess, UpDisconnected, - UpWoke(BlockedTask), + UpWoke(SignalToken), } pub enum SelectionResult { SelSuccess, - SelCanceled(BlockedTask), - SelUpgraded(BlockedTask, Receiver), + SelCanceled, + SelUpgraded(SignalToken, Receiver), } // Any message could contain an "upgrade request" to a new shared port, so the @@ -89,7 +88,6 @@ impl Packet { } } - pub fn send(&mut self, t: T) -> Result<(), T> { // If the other port has deterministically gone away, then definitely // must return the data back up the stack. Otherwise, the data is @@ -98,10 +96,11 @@ impl Packet { match self.do_send(Data(t)) { UpSuccess | UpDisconnected => {}, - UpWoke(task) => { task.wake().map(|t| t.reawaken()); } + UpWoke(token) => { token.signal(); } } Ok(()) } + pub fn upgrade(&mut self, up: Receiver) -> UpgradeResult { // If the port has gone away, then there's no need to proceed any // further. @@ -144,20 +143,20 @@ impl Packet { } // Consumes ownership of the 'to_wake' field. - fn take_to_wake(&mut self) -> BlockedTask { - let task = self.to_wake.load(atomic::SeqCst); + fn take_to_wake(&mut self) -> SignalToken { + let ptr = self.to_wake.load(atomic::SeqCst); self.to_wake.store(0, atomic::SeqCst); - assert!(task != 0); - unsafe { BlockedTask::cast_from_uint(task) } + assert!(ptr != 0); + unsafe { SignaToken::cast_from_uint(ptr) } } // Decrements the count on the channel for a sleeper, returning the sleeper // back if it shouldn't sleep. Note that this is the location where we take // steals into account. - fn decrement(&mut self, task: BlockedTask) -> Result<(), BlockedTask> { + fn decrement(&mut self, token: SignalToken) -> Result<(), SignalToken> { assert_eq!(self.to_wake.load(atomic::SeqCst), 0); - let n = unsafe { task.cast_to_uint() }; - self.to_wake.store(n, atomic::SeqCst); + let ptr = unsafe { token.cast_to_uint() }; + self.to_wake.store(ptr, atomic::SeqCst); let steals = self.steals; self.steals = 0; @@ -173,7 +172,7 @@ impl Packet { } self.to_wake.store(0, atomic::SeqCst); - Err(unsafe { BlockedTask::cast_from_uint(n) }) + Err(unsafe { SignalToken::cast_from_uint(ptr) }) } pub fn recv(&mut self) -> Result> { @@ -185,10 +184,10 @@ impl Packet { // Welp, our channel has no data. Deschedule the current task and // initiate the blocking protocol. - let task: Box = Local::take(); - task.deschedule(1, |task| { - self.decrement(task) - }); + let (wait_token, signal_token) = blocking::tokens(); + if self.decrement(signal_token).is_ok() { + wait_token.wait() + } match self.try_recv() { // Messages which actually popped from the queue shouldn't count as @@ -269,7 +268,7 @@ impl Packet { // Dropping a channel is pretty simple, we just flag it as disconnected // and then wakeup a blocker if there is one. match self.cnt.swap(DISCONNECTED, atomic::SeqCst) { - -1 => { self.take_to_wake().wake().map(|t| t.reawaken()); } + -1 => { self.take_to_wake().signal(); } DISCONNECTED => {} n => { assert!(n >= 0); } } @@ -364,19 +363,19 @@ impl Packet { // Attempts to start selecting on this port. Like a oneshot, this can fail // immediately because of an upgrade. - pub fn start_selection(&mut self, task: BlockedTask) -> SelectionResult { - match self.decrement(task) { + pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult { + match self.decrement(token) { Ok(()) => SelSuccess, - Err(task) => { + Err(token) => { let ret = match self.queue.peek() { Some(&GoUp(..)) => { match self.queue.pop() { - Some(GoUp(port)) => SelUpgraded(task, port), + Some(GoUp(port)) => SelUpgraded(token, port), _ => unreachable!(), } } - Some(..) => SelCanceled(task), - None => SelCanceled(task), + Some(..) => SelCanceled, + None => SelCanceled, }; // Undo our decrement above, and we should be guaranteed that the // previous value is positive because we're not going to sleep @@ -439,7 +438,7 @@ impl Packet { // final solution but rather out of necessity for now to get // something working. if prev < 0 { - self.take_to_wake().trash(); + drop(self.take_to_wake()); } else { while self.to_wake.load(atomic::SeqCst) != 0 { Thread::yield_now(); diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index 933cd43c662aa..7e87596429c6f 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -42,11 +42,10 @@ use alloc::boxed::Box; use vec::Vec; use core::mem; use core::cell::UnsafeCell; -use rt::local::Local; -use rt::mutex::{NativeMutex, LockGuard}; -use rt::task::{Task, BlockedTask}; -use sync::atomic; +use sync::{atomic, Mutex, MutexGuard}; +use comm::blocking::{mod, WaitToken, SignalToken}; +use comm::select::StartResult::{mod, Installed, Abort}; pub struct Packet { /// Only field outside of the mutex. Just done for kicks, but mainly because @@ -74,10 +73,10 @@ struct State { canceled: Option<&'static mut bool>, } -/// Possible flavors of tasks who can be blocked on this channel. +/// Possible flavors of threads who can be blocked on this channel. enum Blocker { - BlockedSender(BlockedTask), - BlockedReceiver(BlockedTask), + BlockedSender(SignalToken), + BlockedReceiver(SignalToken), NoneBlocked } @@ -89,7 +88,7 @@ struct Queue { } struct Node { - task: Option, + token: Option, next: *mut Node, } @@ -106,28 +105,15 @@ pub enum Failure { Disconnected, } -/// Atomically blocks the current task, placing it into `slot`, unlocking `lock` +/// Atomically blocks the current thread, placing it into `slot`, unlocking `lock` /// in the meantime. This re-locks the mutex upon returning. -fn wait(slot: &mut Blocker, f: fn(BlockedTask) -> Blocker, - lock: &NativeMutex) { - let me: Box = Local::take(); - me.deschedule(1, |task| { - match mem::replace(slot, f(task)) { - NoneBlocked => {} - _ => unreachable!(), - } - unsafe { lock.unlock_noguard(); } - Ok(()) - }); - unsafe { lock.lock_noguard(); } -} -/// Wakes up a task, dropping the lock at the correct time -fn wakeup(task: BlockedTask, guard: LockGuard) { +/// Wakes up a thread, dropping the lock at the correct time +fn wakeup(token: SignalToken, guard: MutexGuard>) { // We need to be careful to wake up the waiting task *outside* of the mutex // in case it incurs a context switch. - mem::drop(guard); - task.wake().map(|t| t.reawaken()); + drop(guard); + token.signal(); } impl Packet { @@ -153,29 +139,27 @@ impl Packet { } } - // Locks this channel, returning a guard for the state and the mutable state - // itself. Care should be taken to ensure that the state does not escape the - // guard! - // - // Note that we're ok promoting an & reference to an &mut reference because - // the lock ensures that we're the only ones in the world with a pointer to - // the state. - fn lock<'a>(&'a self) -> (LockGuard<'a>, &'a mut State) { - unsafe { - let guard = self.lock.lock(); - (guard, &mut *self.state.get()) + // wait until a send slot is available, returning locked access to + // the channel state. + fn acquire_send_slot(&self) -> MutexGuard> { + let mut node = Node { token: None, next: 0 as *mut Node }; + loop { + let mut guard = self.lock.lock(); + // are we ready to go? + if guard.disconnected || guard.buf.size() < guard.buf.cap() { + return guard; + } + // no room; actually block + let wait_token = guard.queue.enqueue(&mut node); + drop(guard); + wait_token.wait(); } } pub fn send(&self, t: T) -> Result<(), T> { - let (guard, state) = self.lock(); - - // wait for a slot to become available, and enqueue the data - while !state.disconnected && state.buf.size() == state.buf.cap() { - state.queue.enqueue(&self.lock); - } - if state.disconnected { return Err(t) } - state.buf.enqueue(t); + let guard = self.acquire_send_slot(); + if guard.disconnected { return Err(t) } + guard.buf.enqueue(t); match mem::replace(&mut state.blocker, NoneBlocked) { // if our capacity is 0, then we need to wait for a receiver to be @@ -194,7 +178,7 @@ impl Packet { NoneBlocked => Ok(()), // success, someone's about to receive our buffered data. - BlockedReceiver(task) => { wakeup(task, guard); Ok(()) } + BlockedReceiver(token) => { wakeup(token, guard); Ok(()) } BlockedSender(..) => panic!("lolwut"), } @@ -212,9 +196,9 @@ impl Packet { match mem::replace(&mut state.blocker, NoneBlocked) { NoneBlocked => Err(super::Full(t)), BlockedSender(..) => unreachable!(), - BlockedReceiver(task) => { - state.buf.enqueue(t); - wakeup(task, guard); + BlockedReceiver(token) => { + guard.buf.enqueue(t); + wakeup(token, guard); Ok(()) } } @@ -222,10 +206,10 @@ impl Packet { // If the buffer has some space and the capacity isn't 0, then we // just enqueue the data for later retrieval, ensuring to wake up // any blocked receiver if there is one. - assert!(state.buf.size() < state.buf.cap()); - state.buf.enqueue(t); - match mem::replace(&mut state.blocker, NoneBlocked) { - BlockedReceiver(task) => wakeup(task, guard), + assert!(guard.buf.size() < guard.buf.cap()); + guard.buf.enqueue(t); + match mem::replace(&mut guard.blocker, NoneBlocked) { + BlockedReceiver(token) => wakeup(token, guard), NoneBlocked => {} BlockedSender(..) => unreachable!(), } @@ -238,7 +222,7 @@ impl Packet { // When reading this, remember that there can only ever be one receiver at // time. pub fn recv(&self) -> Result { - let (guard, state) = self.lock(); + let guard = self.lock.lock(); // Wait for the buffer to have something in it. No need for a while loop // because we're the only receiver. @@ -275,10 +259,8 @@ impl Packet { // * `waited` - flag if the receiver blocked to receive some data, or if it // just picked up some data on the way out // * `guard` - the lock guard that is held over this channel's lock - fn wakeup_senders(&self, waited: bool, - guard: LockGuard, - state: &mut State) { - let pending_sender1: Option = state.queue.dequeue(); + fn wakeup_senders(&self, waited: bool, guard: MutexGuard>) { + let pending_sender1: Option = guard.queue.dequeue(); // If this is a no-buffer channel (cap == 0), then if we didn't wait we // need to ACK the sender. If we waited, then the sender waking us up @@ -287,9 +269,9 @@ impl Packet { match mem::replace(&mut state.blocker, NoneBlocked) { NoneBlocked => None, BlockedReceiver(..) => unreachable!(), - BlockedSender(task) => { - state.canceled.take(); - Some(task) + BlockedSender(token) => { + guard.canceled.take(); + Some(token) } } } else { @@ -298,8 +280,8 @@ impl Packet { mem::drop((state, guard)); // only outside of the lock do we wake up the pending tasks - pending_sender1.map(|t| t.wake().map(|t| t.reawaken())); - pending_sender2.map(|t| t.wake().map(|t| t.reawaken())); + pending_sender1.map(|t| t.signal()); + pending_sender2.map(|t| t.signal()); } // Prepares this shared packet for a channel clone, essentially just bumping @@ -322,7 +304,7 @@ impl Packet { match mem::replace(&mut state.blocker, NoneBlocked) { NoneBlocked => {} BlockedSender(..) => unreachable!(), - BlockedReceiver(task) => wakeup(task, guard), + BlockedReceiver(token) => wakeup(token, guard), } } @@ -349,9 +331,9 @@ impl Packet { let waiter = match mem::replace(&mut state.blocker, NoneBlocked) { NoneBlocked => None, - BlockedSender(task) => { - *state.canceled.take().unwrap() = true; - Some(task) + BlockedSender(token) => { + *guard.canceled.take().unwrap() = true; + Some(token) } BlockedReceiver(..) => unreachable!(), }; @@ -359,11 +341,11 @@ impl Packet { loop { match queue.dequeue() { - Some(task) => { task.wake().map(|t| t.reawaken()); } + Some(token) => { token.signal(); } None => break, } } - waiter.map(|t| t.wake().map(|t| t.reawaken())); + waiter.map(|t| t.signal()); } //////////////////////////////////////////////////////////////////////////// @@ -379,17 +361,17 @@ impl Packet { // Attempts to start selection on this port. This can either succeed or fail // because there is data waiting. - pub fn start_selection(&self, task: BlockedTask) -> Result<(), BlockedTask>{ - let (_g, state) = self.lock(); - if state.disconnected || state.buf.size() > 0 { - Err(task) + pub fn start_selection(&self, token: SignalToken) -> StartResult { + let guard = self.lock(); + if guard.disconnected || guard.buf.size() > 0 { + Abort } else { - match mem::replace(&mut state.blocker, BlockedReceiver(task)) { + match mem::replace(&mut guard.blocker, BlockedReceiver(token)) { NoneBlocked => {} BlockedSender(..) => unreachable!(), BlockedReceiver(..) => unreachable!(), } - Ok(()) + Installed } } @@ -401,11 +383,11 @@ impl Packet { let (_g, state) = self.lock(); match mem::replace(&mut state.blocker, NoneBlocked) { NoneBlocked => true, - BlockedSender(task) => { - state.blocker = BlockedSender(task); + BlockedSender(token) => { + guard.blocker = BlockedSender(token); true } - BlockedReceiver(task) => { task.trash(); false } + BlockedReceiver(token) => { drop(token); false } } } } @@ -449,31 +431,25 @@ impl Buffer { //////////////////////////////////////////////////////////////////////////////// impl Queue { - fn enqueue(&mut self, lock: &NativeMutex) { - let task: Box = Local::take(); - let mut node = Node { - task: None, - next: 0 as *mut Node, - }; - task.deschedule(1, |task| { - node.task = Some(task); - if self.tail.is_null() { - self.head = &mut node as *mut Node; - self.tail = &mut node as *mut Node; - } else { - unsafe { - (*self.tail).next = &mut node as *mut Node; - self.tail = &mut node as *mut Node; - } + fn enqueue(&mut self, node: &mut Node) -> WaitToken { + let (wait_token, signal_token) = blocking::tokens(); + node.token = Some(signal_token); + node.next = 0 as *mut Node; + + if self.tail.is_null() { + self.head = node as *mut Node; + self.tail = node as *mut Node; + } else { + unsafe { + (*self.tail).next = node as *mut Node; + self.tail = node as *mut Node; } - unsafe { lock.unlock_noguard(); } - Ok(()) - }); - unsafe { lock.lock_noguard(); } - assert!(node.next.is_null()); + } + + wait_token } - fn dequeue(&mut self) -> Option { + fn dequeue(&mut self) -> Option { if self.head.is_null() { return None } @@ -484,7 +460,7 @@ impl Queue { } unsafe { (*node).next = 0 as *mut Node; - Some((*node).task.take().unwrap()) + Some((*node).token.take().unwrap()) } } } From d8e4780b0b59636cd979a60434a407142e407ac9 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 1 Dec 2014 08:49:32 -0800 Subject: [PATCH 09/24] Remove rt::{mutex, exclusive} --- src/libcollections/slice.rs | 4 +- src/libstd/comm/sync.rs | 111 ++++++++++++++------------ src/libstd/rt/args.rs | 4 +- src/libstd/rt/at_exit_imp.rs | 8 +- src/libstd/rt/mod.rs | 3 - src/libstd/sys/common/thread_local.rs | 5 +- src/libstd/sys/unix/backtrace.rs | 4 +- src/libstd/sys/windows/backtrace.rs | 4 +- 8 files changed, 74 insertions(+), 69 deletions(-) diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index c2ed28d81df72..f875147ed820e 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1627,9 +1627,7 @@ mod tests { #[test] fn test_swap_remove_noncopyable() { // Tests that we don't accidentally run destructors twice. - let mut v = vec![rt::exclusive::Exclusive::new(()), - rt::exclusive::Exclusive::new(()), - rt::exclusive::Exclusive::new(())]; + let mut v = vec![Box::new(()), Box::new(()), Box::new(())]; let mut _e = v.swap_remove(0); assert_eq!(v.len(), 2); _e = v.swap_remove(1); diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index 7e87596429c6f..9e4bdb15b0037 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -52,9 +52,7 @@ pub struct Packet { /// the other shared channel already had the code implemented channels: atomic::AtomicUint, - /// The state field is protected by this mutex - lock: NativeMutex, - state: UnsafeCell>, + lock: Mutex>, } struct State { @@ -107,9 +105,25 @@ pub enum Failure { /// Atomically blocks the current thread, placing it into `slot`, unlocking `lock` /// in the meantime. This re-locks the mutex upon returning. +fn wait<'a, 'b, T>(lock: &'a Mutex>, + guard: MutexGuard<'b, State>, + f: fn(BlockedTask) -> Blocker) + -> MutexGuard<'a, State> +{ + let me: Box = Local::take(); + me.deschedule(1, |task| { + match mem::replace(&mut guard.blocker, f(task)) { + NoneBlocked => {} + _ => unreachable!(), + } + mem::drop(guard); + Ok(()) + }); + lock.lock() +} -/// Wakes up a thread, dropping the lock at the correct time -fn wakeup(token: SignalToken, guard: MutexGuard>) { +/// Wakes up a task, dropping the lock at the correct time +fn wakeup(task: BlockedTask, guard: MutexGuard>) { // We need to be careful to wake up the waiting task *outside* of the mutex // in case it incurs a context switch. drop(guard); @@ -120,8 +134,7 @@ impl Packet { pub fn new(cap: uint) -> Packet { Packet { channels: atomic::AtomicUint::new(1), - lock: unsafe { NativeMutex::new() }, - state: UnsafeCell::new(State { + lock: Mutex::new(State { disconnected: false, blocker: NoneBlocked, cap: cap, @@ -161,17 +174,17 @@ impl Packet { if guard.disconnected { return Err(t) } guard.buf.enqueue(t); - match mem::replace(&mut state.blocker, NoneBlocked) { + match mem::replace(&mut guard.blocker, NoneBlocked) { // if our capacity is 0, then we need to wait for a receiver to be // available to take our data. After waiting, we check again to make // sure the port didn't go away in the meantime. If it did, we need // to hand back our data. - NoneBlocked if state.cap == 0 => { + NoneBlocked if guard.cap == 0 => { let mut canceled = false; - assert!(state.canceled.is_none()); - state.canceled = Some(unsafe { mem::transmute(&mut canceled) }); - wait(&mut state.blocker, BlockedSender, &self.lock); - if canceled {Err(state.buf.dequeue())} else {Ok(())} + assert!(guard.canceled.is_none()); + guard.canceled = Some(unsafe { mem::transmute(&mut canceled) }); + let guard = wait(&self.lock, guard, BlockedSender); + if canceled {Err(guard.buf.dequeue())} else {Ok(())} } // success, we buffered some data @@ -185,15 +198,15 @@ impl Packet { } pub fn try_send(&self, t: T) -> Result<(), super::TrySendError> { - let (guard, state) = self.lock(); - if state.disconnected { + let guard = self.lock.lock(); + if guard.disconnected { Err(super::RecvDisconnected(t)) - } else if state.buf.size() == state.buf.cap() { + } else if guard.buf.size() == guard.buf.cap() { Err(super::Full(t)) - } else if state.cap == 0 { + } else if guard.cap == 0 { // With capacity 0, even though we have buffer space we can't // transfer the data unless there's a receiver waiting. - match mem::replace(&mut state.blocker, NoneBlocked) { + match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => Err(super::Full(t)), BlockedSender(..) => unreachable!(), BlockedReceiver(token) => { @@ -227,28 +240,28 @@ impl Packet { // Wait for the buffer to have something in it. No need for a while loop // because we're the only receiver. let mut waited = false; - if !state.disconnected && state.buf.size() == 0 { - wait(&mut state.blocker, BlockedReceiver, &self.lock); + if !guard.disconnected && guard.buf.size() == 0 { + wait(&mut guard.blocker, BlockedReceiver, &self.lock); waited = true; } - if state.disconnected && state.buf.size() == 0 { return Err(()) } + if guard.disconnected && guard.buf.size() == 0 { return Err(()) } // Pick up the data, wake up our neighbors, and carry on - assert!(state.buf.size() > 0); - let ret = state.buf.dequeue(); + assert!(guard.buf.size() > 0); + let ret = guard.buf.dequeue(); self.wakeup_senders(waited, guard, state); return Ok(ret); } pub fn try_recv(&self) -> Result { - let (guard, state) = self.lock(); + let guard = self.lock(); // Easy cases first - if state.disconnected { return Err(Disconnected) } - if state.buf.size() == 0 { return Err(Empty) } + if guard.disconnected { return Err(Disconnected) } + if guard.buf.size() == 0 { return Err(Empty) } // Be sure to wake up neighbors - let ret = Ok(state.buf.dequeue()); + let ret = Ok(guard.buf.dequeue()); self.wakeup_senders(false, guard, state); return ret; @@ -265,8 +278,8 @@ impl Packet { // If this is a no-buffer channel (cap == 0), then if we didn't wait we // need to ACK the sender. If we waited, then the sender waking us up // was already the ACK. - let pending_sender2 = if state.cap == 0 && !waited { - match mem::replace(&mut state.blocker, NoneBlocked) { + let pending_sender2 = if guard.cap == 0 && !waited { + match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => None, BlockedReceiver(..) => unreachable!(), BlockedSender(token) => { @@ -277,7 +290,7 @@ impl Packet { } else { None }; - mem::drop((state, guard)); + mem::drop(guard); // only outside of the lock do we wake up the pending tasks pending_sender1.map(|t| t.signal()); @@ -298,10 +311,10 @@ impl Packet { } // Not much to do other than wake up a receiver if one's there - let (guard, state) = self.lock(); - if state.disconnected { return } - state.disconnected = true; - match mem::replace(&mut state.blocker, NoneBlocked) { + let guard = self.lock(); + if guard.disconnected { return } + guard.disconnected = true; + match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => {} BlockedSender(..) => unreachable!(), BlockedReceiver(token) => wakeup(token, guard), @@ -309,27 +322,27 @@ impl Packet { } pub fn drop_port(&self) { - let (guard, state) = self.lock(); + let guard = self.lock(); - if state.disconnected { return } - state.disconnected = true; + if guard.disconnected { return } + guard.disconnected = true; // If the capacity is 0, then the sender may want its data back after // we're disconnected. Otherwise it's now our responsibility to destroy // the buffered data. As with many other portions of this code, this // needs to be careful to destroy the data *outside* of the lock to // prevent deadlock. - let _data = if state.cap != 0 { - mem::replace(&mut state.buf.buf, Vec::new()) + let _data = if guard.cap != 0 { + mem::replace(&mut guard.buf.buf, Vec::new()) } else { Vec::new() }; - let mut queue = mem::replace(&mut state.queue, Queue { + let mut queue = mem::replace(&mut guard.queue, Queue { head: 0 as *mut Node, tail: 0 as *mut Node, }); - let waiter = match mem::replace(&mut state.blocker, NoneBlocked) { + let waiter = match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => None, BlockedSender(token) => { *guard.canceled.take().unwrap() = true; @@ -337,7 +350,7 @@ impl Packet { } BlockedReceiver(..) => unreachable!(), }; - mem::drop((state, guard)); + mem::drop(guard); loop { match queue.dequeue() { @@ -355,8 +368,8 @@ impl Packet { // If Ok, the value is whether this port has data, if Err, then the upgraded // port needs to be checked instead of this one. pub fn can_recv(&self) -> bool { - let (_g, state) = self.lock(); - state.disconnected || state.buf.size() > 0 + let guard = self.lock(); + guard.disconnected || guard.buf.size() > 0 } // Attempts to start selection on this port. This can either succeed or fail @@ -380,8 +393,8 @@ impl Packet { // // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { - let (_g, state) = self.lock(); - match mem::replace(&mut state.blocker, NoneBlocked) { + let guard = self.lock(); + match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => true, BlockedSender(token) => { guard.blocker = BlockedSender(token); @@ -396,9 +409,9 @@ impl Packet { impl Drop for Packet { fn drop(&mut self) { assert_eq!(self.channels.load(atomic::SeqCst), 0); - let (_g, state) = self.lock(); - assert!(state.queue.dequeue().is_none()); - assert!(state.canceled.is_none()); + let guard = self.lock(); + assert!(guard.queue.dequeue().is_none()); + assert!(guard.canceled.is_none()); } } diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index 93c956fc3c54a..fed0554863c6a 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -51,10 +51,10 @@ mod imp { use string::String; use mem; - use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; + use sync::mutex::{StaticMutex, MUTEX_INIT}; static mut GLOBAL_ARGS_PTR: uint = 0; - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + static LOCK: NativeMutex = MUTEX_INIT; pub unsafe fn init(argc: int, argv: *const *const u8) { let args = load_argc_and_argv(argc, argv); diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index 9ddb59bfffcf5..b8012134c9ec1 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -17,20 +17,18 @@ use core::prelude::*; use libc; use boxed::Box; use vec::Vec; -use sync::{atomic, Once, ONCE_INIT}; +use sync::{Mutex, atomic, Once, ONCE_INIT}; use mem; use thunk::Thunk; -use rt::exclusive::Exclusive; - -type Queue = Exclusive>; +type Queue = Mutex>; static INIT: Once = ONCE_INIT; static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; static RUNNING: atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL; fn init() { - let state: Box = box Exclusive::new(Vec::new()); + let state: Box = box Mutex::new(Vec::new()); unsafe { QUEUE.store(mem::transmute(state), atomic::SeqCst); libc::atexit(run); diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 8ef7ac43a30ef..a3b1d831a385e 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -71,9 +71,6 @@ pub mod backtrace; mod macros; // These should be refactored/moved/made private over time -pub mod mutex; -pub mod thread; -pub mod exclusive; pub mod util; <<<<<<< HEAD ======= diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs index a8bc6bf9d0d6a..c3bf5cfc301c6 100644 --- a/src/libstd/sys/common/thread_local.rs +++ b/src/libstd/sys/common/thread_local.rs @@ -58,10 +58,9 @@ use prelude::*; -use rt::exclusive::Exclusive; use rt; use sync::atomic::{mod, AtomicUint}; -use sync::{Once, ONCE_INIT}; +use sync::{Mutex, Once, ONCE_INIT}; use sys::thread_local as imp; @@ -143,7 +142,7 @@ pub const INIT_INNER: StaticKeyInner = StaticKeyInner { }; static INIT_KEYS: Once = ONCE_INIT; -static mut KEYS: *mut Exclusive> = 0 as *mut _; +static mut KEYS: *mut Mutex> = 0 as *mut _; impl StaticKey { /// Gets the value associated with this TLS key diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index c139dba2c4608..2b36ece0e4b48 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -89,7 +89,7 @@ use libc; use mem; use option::{Some, None, Option}; use result::{Ok, Err}; -use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use sync::{StaticMutex, MUTEX_INIT}; use sys_common::backtrace::*; @@ -150,7 +150,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> { // is semi-reasonable in terms of printing anyway, and we know that all // I/O done here is blocking I/O, not green I/O, so we don't have to // worry about this being a native vs green mutex. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + static LOCK: StaticMutex = MUTEX_INIT; let _g = unsafe { LOCK.lock() }; try!(writeln!(w, "stack backtrace:")); diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 833b69d6cbebb..9903d2f1ae281 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -30,7 +30,7 @@ use ops::Drop; use option::{Some, None}; use path::Path; use result::{Ok, Err}; -use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +use sync::{StaticMutex, MUTEX_INIT}; use slice::SliceExt; use str::StrPrelude; use dynamic_lib::DynamicLibrary; @@ -293,7 +293,7 @@ impl Drop for Cleanup { pub fn write(w: &mut Writer) -> IoResult<()> { // According to windows documentation, all dbghelp functions are // single-threaded. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + static LOCK: StaticMutex = MUTEX_INIT; let _g = unsafe { LOCK.lock() }; // Open up dbghelp.dll, we don't link to it explicitly because it can't From 14c1a103bc3f78721df1dc860a75a477c8275e3a Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 7 Dec 2014 00:32:50 -0800 Subject: [PATCH 10/24] Revise rt::unwind --- src/libstd/rt/mod.rs | 4 --- src/libstd/rt/unwind.rs | 42 ++++------------------------ src/libstd/sys/common/thread_info.rs | 30 ++++++++++++++++---- 3 files changed, 30 insertions(+), 46 deletions(-) diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index a3b1d831a385e..44794d2b957ce 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -72,10 +72,6 @@ mod macros; // These should be refactored/moved/made private over time pub mod util; -<<<<<<< HEAD -======= -pub mod task; ->>>>>>> Remove rt::{local, local_data, thread_local_storage} pub mod unwind; mod args; diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 1ac06270851c5..decf7cfb60a27 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -72,16 +72,9 @@ use mem; use raw::Closure; use libc::c_void; -use rt::local::Local; -use rt::task::Task; - +use sys_common::thread_info; use rt::libunwind as uw; -#[allow(missing_copy_implementations)] -pub struct Unwinder { - unwinding: bool, -} - struct Exception { uwe: uw::_Unwind_Exception, cause: Option>, @@ -104,18 +97,6 @@ static CALLBACKS: [atomic::AtomicUint, ..MAX_CALLBACKS] = atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT]; static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; -impl Unwinder { - pub fn new() -> Unwinder { - Unwinder { - unwinding: false, - } - } - - pub fn unwinding(&self) -> bool { - self.unwinding - } -} - /// Invoke a closure, capturing the cause of panic if one occurs. /// /// This function will return `None` if the closure did not panic, and will @@ -556,7 +537,7 @@ pub fn begin_unwind(msg: M, file_line: &(&'static str, uint)) -> /// we need the `Any` object anyway, we're not just creating it to /// avoid being generic.) /// -/// Do this split took the LLVM IR line counts of `fn main() { panic!() +/// Doing this split took the LLVM IR line counts of `fn main() { panic!() /// }` from ~1900/3700 (-O/no opts) to 180/590. #[inline(never)] #[cold] // this is the slow path, please never inline this fn begin_unwind_inner(msg: Box, file_line: &(&'static str, uint)) -> ! { @@ -583,27 +564,16 @@ fn begin_unwind_inner(msg: Box, file_line: &(&'static str, uint)) -> }; // 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 mut task: Box = match Local::try_take() { - Some(task) => task, - None => rust_panic(msg), - }; - - if task.unwinder.unwinding { - // If a task panics while it's already unwinding then we + // perform the unwinding. + if thread_info::unwinding() { + // If a thread panics 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() } } - task.unwinder.unwinding = true; - - // 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); + thread_info::set_unwinding(true); rust_panic(msg); } diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index fb0231b44ba73..f88fd49ab99f5 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -8,30 +8,36 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::prelude::*; + +use thread::Thread; +use cell::RefCell; +use string::String; + struct ThreadInfo { // This field holds the known bounds of the stack in (lo, hi) // form. Not all threads necessarily know their precise bounds, // hence this is optional. stack_bounds: (uint, uint), stack_guard: uint, - unwinder: Unwinder, + unwinding: bool, thread: Thread, } -thread_local!(static THREAD_INFO: RefCell> = RefCell::new(None)); +thread_local!(static THREAD_INFO: RefCell> = RefCell::new(None)) impl ThreadInfo { - fn with(f: |&ThreadInfo| -> R) -> R { + fn with(f: |&mut ThreadInfo| -> R) -> R { THREAD_INFO.with(|c| { if c.borrow().is_none() { *c.borrow_mut() = Some(ThreadInfo { stack_bounds: (0, 0), stack_guard: 0, - unwinder: Unwinder::new(), + unwinder: false, thread: Thread::new(None), }) } - f(c.borrow().as_ref().unwrap()) + f(c.borrow_mut().as_ref().unwrap()) }) } } @@ -44,12 +50,24 @@ pub fn panicking() -> bool { ThreadInfo::with(|info| info.unwinder.unwinding()) } +pub fn stack_guard() -> uint { + ThreadInfo::with(|info| info.stack_guard) +} + +pub fn unwinding() -> bool { + ThreadInfo::with(|info| info.unwinder.unwinding) +} + +pub fn set_unwinding(unwinding: bool) { + ThreadInfo::with(|info| info.unwinding = unwinding) +} + pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { THREAD_INFO.with(|c| assert!(c.borrow().is_none())); THREAD_INFO.with(|c| *c.borrow_mut() = Some(ThreadInfo{ stack_bounds: stack_bounds, stack_guard: stack_guard, - unwinder: Unwinder::new(), + unwinding: false, thread: thread, })); } From 43ae4b3301cc0605839778ecf59effb32b752e33 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sat, 6 Dec 2014 18:34:37 -0800 Subject: [PATCH 11/24] Fallout from new thread API --- src/compiletest/runtest.rs | 6 +- src/doc/guide-tasks.md | 6 +- src/libcore/borrow.rs | 2 +- src/librustc_driver/lib.rs | 15 ++- src/librustc_trans/back/write.rs | 15 ++- src/librustdoc/lib.rs | 5 +- src/librustdoc/test.rs | 3 +- src/libstd/c_str.rs | 4 +- src/libstd/comm/blocking.rs | 6 +- src/libstd/comm/mod.rs | 121 +++++------------- src/libstd/comm/oneshot.rs | 7 +- src/libstd/comm/select.rs | 23 ++-- src/libstd/comm/shared.rs | 2 +- src/libstd/comm/stream.rs | 5 +- src/libstd/comm/sync.rs | 40 +++--- src/libstd/failure.rs | 84 ++++-------- src/libstd/io/comm_adapters.rs | 10 +- src/libstd/io/net/pipe.rs | 2 +- src/libstd/io/net/tcp.rs | 4 +- src/libstd/io/process.rs | 11 +- src/libstd/io/stdio.rs | 33 ++--- src/libstd/lib.rs | 2 + src/libstd/os.rs | 2 +- src/libstd/path/posix.rs | 14 +- src/libstd/path/windows.rs | 14 +- src/libstd/rand/os.rs | 15 ++- src/libstd/rt/at_exit_imp.rs | 33 +++-- src/libstd/rt/backtrace.rs | 4 - src/libstd/rt/mod.rs | 38 ++---- src/libstd/rt/unwind.rs | 2 +- src/libstd/rt/util.rs | 12 +- src/libstd/sync/future.rs | 4 +- src/libstd/sync/mutex.rs | 10 +- src/libstd/sync/once.rs | 4 +- src/libstd/sync/poison.rs | 18 +-- src/libstd/sync/rwlock.rs | 34 ++--- src/libstd/sync/task_pool.rs | 7 +- src/libstd/sys/common/backtrace.rs | 2 +- src/libstd/sys/common/helper_thread.rs | 6 +- src/libstd/sys/common/mod.rs | 1 + src/libstd/sys/common/thread_info.rs | 15 +-- src/libstd/sys/common/thread_local.rs | 1 - src/libstd/sys/unix/backtrace.rs | 2 +- src/libstd/sys/unix/stack_overflow.rs | 32 ++--- src/libstd/sys/windows/stack_overflow.rs | 7 +- src/libstd/task.rs | 15 +++ src/libstd/thread.rs | 27 ++-- src/libstd/thread_local/mod.rs | 6 +- src/libtest/lib.rs | 17 ++- ...nup-rvalue-temp-during-incomplete-alloc.rs | 4 +- src/test/run-pass/task-stderr.rs | 6 +- 51 files changed, 321 insertions(+), 437 deletions(-) diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index ea6f180ec39ac..1b9d98823b036 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -32,7 +32,7 @@ use std::io; use std::os; use std::str; use std::string::String; -use std::task; +use std::thread::Thread; use std::time::Duration; use test::MetricMap; @@ -445,9 +445,9 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { loop { //waiting 1 second for gdbserver start timer::sleep(Duration::milliseconds(1000)); - let result = task::try(move || { + let result = Thread::with_join(move || { tcp::TcpStream::connect("127.0.0.1:5039").unwrap(); - }); + }).join(); if result.is_err() { continue; } diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index 4adca43be18e9..4753ba7fe4703 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -347,16 +347,16 @@ result with an `int` field (representing a successful result) or an `Err` result (representing termination with an error). ```{rust} -# use std::task; +# use std::thread::Thread; # fn some_condition() -> bool { false } # fn calculate_result() -> int { 0 } -let result: Result> = task::try(move || { +let result: Result> = Thread::with_join(move || { if some_condition() { calculate_result() } else { panic!("oops!"); } -}); +}).join(); assert!(result.is_err()); ``` diff --git a/src/libcore/borrow.rs b/src/libcore/borrow.rs index b44b87bd93807..4bf356fb552e1 100644 --- a/src/libcore/borrow.rs +++ b/src/libcore/borrow.rs @@ -92,7 +92,7 @@ impl<'a, T, Sized? B> BorrowFrom> for B where B: ToOwned { /// Trait for moving into a `Cow` pub trait IntoCow<'a, T, Sized? B> { - /// Moves `self` into `Cow` + /// Moves `serlf` into `Cow` fn into_cow(self) -> Cow<'a, T, B>; } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index b0f8b3bdbe7df..ba5157932b61b 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -55,7 +55,7 @@ use rustc::DIAGNOSTICS; use std::any::AnyRefExt; use std::io; use std::os; -use std::task::TaskBuilder; +use std::thread; use rustc::session::early_error; @@ -475,18 +475,22 @@ pub fn monitor(f: F) { static STACK_SIZE: uint = 32000000; // 32MB let (tx, rx) = channel(); - let w = io::ChanWriter::new(tx); + let mut w = Some(io::ChanWriter::new(tx)); // option dance let mut r = io::ChanReader::new(rx); - let mut task = TaskBuilder::new().named("rustc").stderr(box w); + let mut cfg = thread::cfg().name("rustc".to_string()); // FIXME: Hacks on hacks. If the env is trying to override the stack size // then *don't* set it explicitly. if os::getenv("RUST_MIN_STACK").is_none() { - task = task.stack_size(STACK_SIZE); + cfg = cfg.stack_size(STACK_SIZE); } - match task.try(f) { + let f = proc() { + std::io::stdio::set_stderr(box w.take().unwrap()); + f() + }; + match cfg.with_join(f).join() { Ok(()) => { /* fallthrough */ } Err(value) => { // Task panicked without emitting a fatal diagnostic @@ -540,4 +544,3 @@ pub fn main() { let result = run(args); std::os::set_exit_status(result); } - diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 24dfe600f2a56..8ec010a85538a 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -30,7 +30,7 @@ use std::ptr; use std::str; use std::mem; use std::sync::{Arc, Mutex}; -use std::task::TaskBuilder; +use std::thread; use libc::{c_uint, c_int, c_void}; #[deriving(Clone, PartialEq, PartialOrd, Ord, Eq)] @@ -896,7 +896,11 @@ fn run_work_multithreaded(sess: &Session, let diag_emitter = diag_emitter.clone(); let remark = sess.opts.cg.remark.clone(); - let future = TaskBuilder::new().named(format!("codegen-{}", i)).try_future(move |:| { + let (tx, rx) = channel(); + let mut tx = Some(tx); + futures.push(rx); + + thread::cfg().name(format!("codegen-{}", i)).spawn(move |:| { let diag_handler = mk_handler(box diag_emitter); // Must construct cgcx inside the proc because it has non-Send @@ -921,13 +925,14 @@ fn run_work_multithreaded(sess: &Session, None => break, } } + + tx.take().unwrap().send(()); }); - futures.push(future); } let mut panicked = false; - for future in futures.into_iter() { - match future.into_inner() { + for rx in futures.into_iter() { + match rx.recv_opt() { Ok(()) => {}, Err(_) => { panicked = true; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 3d51a6d6ab077..9e06b4e88f586 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -342,10 +342,11 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche let cr = Path::new(cratefile); info!("starting to run rustc"); - let (mut krate, analysis) = std::task::try(move |:| { + + let (mut krate, analysis) = std::thread::Thread::with_join(move |:| { let cr = cr; core::run_core(libs, cfgs, externs, &cr, triple) - }).map_err(|_| "rustc failed").unwrap(); + }).join().map_err(|_| "rustc failed").unwrap(); info!("finished with rustc"); let mut analysis = Some(analysis); ANALYSISKEY.with(|s| { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 0c64a0d29dffe..d004f030f36af 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -16,6 +16,7 @@ use std::os; use std::str; use std::string::String; use std::thunk::Thunk; +use std::thread::Thread; use std::collections::{HashSet, HashMap}; use testing; @@ -143,7 +144,7 @@ fn runtest(test: &str, cratename: &str, libs: Vec, externs: core::Externs, let w1 = io::ChanWriter::new(tx); let w2 = w1.clone(); let old = io::stdio::set_stderr(box w1); - spawn(move |:| { + Thread::spawn(move |:| { let mut p = io::ChanReader::new(rx); let mut err = match old { Some(old) => { diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 27a139835c980..6bfb219d9c44e 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -536,7 +536,7 @@ pub unsafe fn from_c_multistring(buf: *const libc::c_char, mod tests { use prelude::*; use ptr; - use task; + use thread::Thread; use libc; use super::*; @@ -637,7 +637,7 @@ mod tests { #[test] fn test_to_c_str_fail() { - assert!(task::try(move|| { "he\x00llo".to_c_str() }).is_err()); + assert!(Thread::with_join(move|| { "he\x00llo".to_c_str() }).join().is_err()); } #[test] diff --git a/src/libstd/comm/blocking.rs b/src/libstd/comm/blocking.rs index 5e9a01d0151d0..bb09726575638 100644 --- a/src/libstd/comm/blocking.rs +++ b/src/libstd/comm/blocking.rs @@ -32,7 +32,7 @@ pub struct WaitToken { no_send: NoSend, } -fn token() -> (WaitToken, SignalToken) { +pub fn tokens() -> (WaitToken, SignalToken) { let inner = Arc::new(Inner { thread: Thread::current(), woken: INIT_ATOMIC_BOOL, @@ -48,7 +48,7 @@ fn token() -> (WaitToken, SignalToken) { } impl SignalToken { - fn signal(&self) -> bool { + pub fn signal(&self) -> bool { let wake = !self.inner.woken.compare_and_swap(false, true, Ordering::SeqCst); if wake { self.inner.thread.unpark(); @@ -73,7 +73,7 @@ impl SignalToken { } impl WaitToken { - fn wait(self) { + pub fn wait(self) { while !self.inner.woken.load(Ordering::SeqCst) { Thread::park() } diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index e5ec0078c5ef0..236a055b91ea7 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -317,8 +317,10 @@ use core::kinds::marker; use core::mem; use core::cell::UnsafeCell; -pub use comm::select::{Select, Handle}; -use comm::select::StartResult::*; +pub use self::select::{Select, Handle}; +use self::select::StartResult; +use self::select::StartResult::*; +use self::blocking::SignalToken; macro_rules! test { { fn $name:ident() $b:block $(#[$a:meta])*} => ( @@ -330,7 +332,7 @@ macro_rules! test { use comm::*; use super::*; - use task; + use thread::Thread; $(#[$a])* #[test] fn f() { $b } } @@ -593,12 +595,12 @@ impl Sender { (a, ret) } oneshot::UpDisconnected => (a, Err(t)), - oneshot::UpWoke(task) => { - // This send cannot panic because the task is + oneshot::UpWoke(token) => { + // This send cannot panic because the thread is // asleep (we're looking at it), so the receiver // can't go away. (*a.get()).send(t).ok().unwrap(); - task.wake().map(|t| t.reawaken()); + token.signal(); (a, Ok(())) } } @@ -937,7 +939,7 @@ impl select::Packet for Receiver { } } - fn start_selection(&self, mut token: SignalToken) -> bool { + fn start_selection(&self, mut token: SignalToken) -> StartResult { loop { let (t, new_port) = match *unsafe { self.inner() } { Oneshot(ref p) => { @@ -1240,11 +1242,11 @@ mod test { test! { fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will panic - let res = task::try(move|| { + let res = Thread::with_join(move|| { let (tx, rx) = channel::(); drop(tx); rx.recv(); - }); + }).join(); // What is our res? assert!(res.is_err()); } } @@ -1312,9 +1314,9 @@ mod test { spawn(move|| { drop(tx); }); - let res = task::try(move|| { + let res = Thread::with_join(move|| { assert!(rx.recv() == box 10); - }); + }).join(); assert!(res.is_err()); } } @@ -1334,19 +1336,19 @@ mod test { spawn(move|| { drop(rx); }); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { tx.send(1); - }); + }).join(); } } } test! { fn oneshot_multi_thread_recv_close_stress() { for _ in range(0, stress_factor()) { let (tx, rx) = channel::(); - spawn(move|| { - let res = task::try(move|| { + spawn(proc() { + let res = Thread::with_join(move|| { rx.recv(); - }); + }).join(); assert!(res.is_err()); }); spawn(move|| { @@ -1495,7 +1497,7 @@ mod test { tx2.send(()); }); // make sure the other task has gone to sleep - for _ in range(0u, 5000) { task::deschedule(); } + for _ in range(0u, 5000) { Thread::yield_now(); } // upgrade to a shared chan and send a message let t = tx.clone(); @@ -1504,45 +1506,7 @@ mod test { // wait for the child task to exit before we exit rx2.recv(); - } } - - test! { fn sends_off_the_runtime() { - use rt::thread::Thread; - - let (tx, rx) = channel(); - let t = Thread::start(move|| { - for _ in range(0u, 1000) { - tx.send(()); - } - }); - for _ in range(0u, 1000) { - rx.recv(); - } - t.join(); - } } - - test! { fn try_recvs_off_the_runtime() { - use rt::thread::Thread; - - let (tx, rx) = channel(); - let (cdone, pdone) = channel(); - let t = Thread::start(move|| { - let mut hits = 0u; - while hits < 10 { - match rx.try_recv() { - Ok(()) => { hits += 1; } - Err(Empty) => { Thread::yield_now(); } - Err(Disconnected) => return, - } - } - cdone.send(()); - }); - for _ in range(0u, 10) { - tx.send(()); - } - t.join(); - pdone.recv(); - } } + }) } #[cfg(test)] @@ -1700,11 +1664,11 @@ mod sync_tests { test! { fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will panic - let res = task::try(move|| { + let res = Thread::with_join(move|| { let (tx, rx) = sync_channel::(0); drop(tx); rx.recv(); - }); + }).join(); // What is our res? assert!(res.is_err()); } } @@ -1777,9 +1741,9 @@ mod sync_tests { spawn(move|| { drop(tx); }); - let res = task::try(move|| { + let res = Thread::with_join(move|| { assert!(rx.recv() == box 10); - }); + }).join(); assert!(res.is_err()); } } @@ -1799,19 +1763,19 @@ mod sync_tests { spawn(move|| { drop(rx); }); - let _ = task::try(move|| { + let _ = Thread::with_join(move || { tx.send(1); - }); + }).join(); } } } test! { fn oneshot_multi_thread_recv_close_stress() { for _ in range(0, stress_factor()) { let (tx, rx) = sync_channel::(0); - spawn(move|| { - let res = task::try(move|| { + spawn(proc() { + let res = Thread::with_join(move|| { rx.recv(); - }); + }).join(); assert!(res.is_err()); }); spawn(move|| { @@ -1960,7 +1924,7 @@ mod sync_tests { tx2.send(()); }); // make sure the other task has gone to sleep - for _ in range(0u, 5000) { task::deschedule(); } + for _ in range(0u, 5000) { Thread::yield_now(); } // upgrade to a shared chan and send a message let t = tx.clone(); @@ -1971,29 +1935,6 @@ mod sync_tests { rx2.recv(); } } - test! { fn try_recvs_off_the_runtime() { - use rt::thread::Thread; - - let (tx, rx) = sync_channel::<()>(0); - let (cdone, pdone) = channel(); - let t = Thread::start(move|| { - let mut hits = 0u; - while hits < 10 { - match rx.try_recv() { - Ok(()) => { hits += 1; } - Err(Empty) => { Thread::yield_now(); } - Err(Disconnected) => return, - } - } - cdone.send(()); - }); - for _ in range(0u, 10) { - tx.send(()); - } - t.join(); - pdone.recv(); - } } - test! { fn send_opt1() { let (tx, rx) = sync_channel::(0); spawn(move|| { rx.recv(); }); @@ -2052,7 +1993,7 @@ mod sync_tests { test! { fn try_send4() { let (tx, rx) = sync_channel::(0); spawn(move|| { - for _ in range(0u, 1000) { task::deschedule(); } + for _ in range(0u, 1000) { Thread::yield_now(); } assert_eq!(tx.try_send(1), Ok(())); }); assert_eq!(rx.recv(), 1); diff --git a/src/libstd/comm/oneshot.rs b/src/libstd/comm/oneshot.rs index 68f3f229cb487..9c5a651884530 100644 --- a/src/libstd/comm/oneshot.rs +++ b/src/libstd/comm/oneshot.rs @@ -39,9 +39,8 @@ use self::MyUpgrade::*; use core::prelude::*; -use alloc::boxed::Box; use comm::Receiver; -use comm::blocking::{mod, WaitToken, SignalToken}; +use comm::blocking::{mod, SignalToken}; use core::mem; use sync::atomic; @@ -143,7 +142,7 @@ impl Packet { // Attempt to not block the task (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(atomic::SeqCst) == EMPTY { - let (wait_token, signal_token) = blocking::token(); + let (wait_token, signal_token) = blocking::tokens(); let ptr = unsafe { signal_token.cast_to_uint() }; // race with senders to enter the blocking state @@ -332,7 +331,7 @@ impl Packet { // If we've got a blocked task, then use an atomic to gain ownership // of it (may fail) - BLOCKED => self.state.compare_and_swap(BLOCKED, EMPTY, atomic::SeqCst) + ptr => self.state.compare_and_swap(ptr, EMPTY, atomic::SeqCst) }; // Now that we've got ownership of our state, figure out what to do diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index 536d38c6e5508..690b5861c2239 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -54,7 +54,6 @@ use core::prelude::*; -use alloc::boxed::Box; use core::cell::Cell; use core::kinds::marker; use core::mem; @@ -63,8 +62,6 @@ use core::uint; use comm::Receiver; use comm::blocking::{mod, SignalToken}; -use self::StartResult::*; - /// The "receiver set" of the select interface. This structure is used to manage /// a set of receivers which are being selected over. pub struct Select { @@ -190,8 +187,8 @@ impl Select { let (wait_token, signal_token) = blocking::tokens(); for (i, handle) in self.iter().enumerate() { match (*handle).packet.start_selection(signal_token.clone()) { - Installed => {} - Abort => { + StartResult::Installed => {} + StartResult::Abort => { // Go back and abort the already-begun selections for handle in self.iter().take(i) { (*handle).packet.abort_selection(); @@ -417,10 +414,10 @@ mod test { let (tx3, rx3) = channel::(); spawn(move|| { - for _ in range(0u, 20) { task::deschedule(); } + for _ in range(0u, 20) { Thread::yield_now(); } tx1.send(1); rx3.recv(); - for _ in range(0u, 20) { task::deschedule(); } + for _ in range(0u, 20) { Thread::yield_now(); } }); select! { @@ -440,7 +437,7 @@ mod test { let (tx3, rx3) = channel::<()>(); spawn(move|| { - for _ in range(0u, 20) { task::deschedule(); } + for _ in range(0u, 20) { Thread::yield_now(); } tx1.send(1); tx2.send(2); rx3.recv(); @@ -541,7 +538,7 @@ mod test { tx3.send(()); }); - for _ in range(0u, 1000) { task::deschedule(); } + for _ in range(0u, 1000) { Thread::yield_now(); } drop(tx1.clone()); tx2.send(()); rx3.recv(); @@ -644,7 +641,7 @@ mod test { tx2.send(()); }); - for _ in range(0u, 100) { task::deschedule() } + for _ in range(0u, 100) { Thread::yield_now() } tx1.send(()); rx2.recv(); } } @@ -663,7 +660,7 @@ mod test { tx2.send(()); }); - for _ in range(0u, 100) { task::deschedule() } + for _ in range(0u, 100) { Thread::yield_now() } tx1.send(()); rx2.recv(); } } @@ -681,7 +678,7 @@ mod test { tx2.send(()); }); - for _ in range(0u, 100) { task::deschedule() } + for _ in range(0u, 100) { Thread::yield_now() } tx1.send(()); rx2.recv(); } } @@ -697,7 +694,7 @@ mod test { test! { fn sync2() { let (tx, rx) = sync_channel::(0); spawn(move|| { - for _ in range(0u, 100) { task::deschedule() } + for _ in range(0u, 100) { Thread::yield_now() } tx.send(1); }); select! { diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs index 1f1ea2ca9a1b2..1022694e634f6 100644 --- a/src/libstd/comm/shared.rs +++ b/src/libstd/comm/shared.rs @@ -22,7 +22,6 @@ pub use self::Failure::*; use core::prelude::*; -use alloc::boxed::Box; use core::cmp; use core::int; @@ -31,6 +30,7 @@ use comm::mpsc_queue as mpsc; use comm::blocking::{mod, SignalToken}; use comm::select::StartResult; use comm::select::StartResult::*; +use thread::Thread; const DISCONNECTED: int = int::MIN; const FUDGE: int = 1024; diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs index a15366d5ebc0c..b68f626060eda 100644 --- a/src/libstd/comm/stream.rs +++ b/src/libstd/comm/stream.rs @@ -24,7 +24,6 @@ use self::Message::*; use core::prelude::*; -use alloc::boxed::Box; use core::cmp; use core::int; use thread::Thread; @@ -32,7 +31,7 @@ use thread::Thread; use sync::atomic; use comm::spsc_queue as spsc; use comm::Receiver; -use comm::blocking::{mod, WaitToken, SignalToken}; +use comm::blocking::{mod, SignalToken}; const DISCONNECTED: int = int::MIN; #[cfg(test)] @@ -147,7 +146,7 @@ impl Packet { let ptr = self.to_wake.load(atomic::SeqCst); self.to_wake.store(0, atomic::SeqCst); assert!(ptr != 0); - unsafe { SignaToken::cast_from_uint(ptr) } + unsafe { SignalToken::cast_from_uint(ptr) } } // Decrements the count on the channel for a sleeper, returning the sleeper diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index 9e4bdb15b0037..b24c6d21fba6d 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -38,10 +38,8 @@ use core::prelude::*; pub use self::Failure::*; use self::Blocker::*; -use alloc::boxed::Box; use vec::Vec; use core::mem; -use core::cell::UnsafeCell; use sync::{atomic, Mutex, MutexGuard}; use comm::blocking::{mod, WaitToken, SignalToken}; @@ -105,10 +103,10 @@ pub enum Failure { /// Atomically blocks the current thread, placing it into `slot`, unlocking `lock` /// in the meantime. This re-locks the mutex upon returning. -fn wait<'a, 'b, T>(lock: &'a Mutex>, - guard: MutexGuard<'b, State>, - f: fn(BlockedTask) -> Blocker) - -> MutexGuard<'a, State> +fn wait<'a, 'b, T: Send>(lock: &'a Mutex>, + mut guard: MutexGuard<'b, State>, + f: fn(SignalToken) -> Blocker) + -> MutexGuard<'a, State> { let me: Box = Local::take(); me.deschedule(1, |task| { @@ -170,7 +168,7 @@ impl Packet { } pub fn send(&self, t: T) -> Result<(), T> { - let guard = self.acquire_send_slot(); + let mut guard = self.acquire_send_slot(); if guard.disconnected { return Err(t) } guard.buf.enqueue(t); @@ -183,7 +181,7 @@ impl Packet { let mut canceled = false; assert!(guard.canceled.is_none()); guard.canceled = Some(unsafe { mem::transmute(&mut canceled) }); - let guard = wait(&self.lock, guard, BlockedSender); + let mut guard = wait(&self.lock, guard, BlockedSender); if canceled {Err(guard.buf.dequeue())} else {Ok(())} } @@ -198,7 +196,7 @@ impl Packet { } pub fn try_send(&self, t: T) -> Result<(), super::TrySendError> { - let guard = self.lock.lock(); + let mut guard = self.lock.lock(); if guard.disconnected { Err(super::RecvDisconnected(t)) } else if guard.buf.size() == guard.buf.cap() { @@ -235,13 +233,13 @@ impl Packet { // When reading this, remember that there can only ever be one receiver at // time. pub fn recv(&self) -> Result { - let guard = self.lock.lock(); + let mut guard = self.lock.lock(); // Wait for the buffer to have something in it. No need for a while loop // because we're the only receiver. let mut waited = false; if !guard.disconnected && guard.buf.size() == 0 { - wait(&mut guard.blocker, BlockedReceiver, &self.lock); + guard = wait(&self.lock, guard, BlockedReceiver); waited = true; } if guard.disconnected && guard.buf.size() == 0 { return Err(()) } @@ -249,12 +247,12 @@ impl Packet { // Pick up the data, wake up our neighbors, and carry on assert!(guard.buf.size() > 0); let ret = guard.buf.dequeue(); - self.wakeup_senders(waited, guard, state); + self.wakeup_senders(waited, guard); return Ok(ret); } pub fn try_recv(&self) -> Result { - let guard = self.lock(); + let mut guard = self.lock.lock(); // Easy cases first if guard.disconnected { return Err(Disconnected) } @@ -262,7 +260,7 @@ impl Packet { // Be sure to wake up neighbors let ret = Ok(guard.buf.dequeue()); - self.wakeup_senders(false, guard, state); + self.wakeup_senders(false, guard); return ret; } @@ -272,7 +270,7 @@ impl Packet { // * `waited` - flag if the receiver blocked to receive some data, or if it // just picked up some data on the way out // * `guard` - the lock guard that is held over this channel's lock - fn wakeup_senders(&self, waited: bool, guard: MutexGuard>) { + fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard>) { let pending_sender1: Option = guard.queue.dequeue(); // If this is a no-buffer channel (cap == 0), then if we didn't wait we @@ -311,7 +309,7 @@ impl Packet { } // Not much to do other than wake up a receiver if one's there - let guard = self.lock(); + let mut guard = self.lock.lock(); if guard.disconnected { return } guard.disconnected = true; match mem::replace(&mut guard.blocker, NoneBlocked) { @@ -322,7 +320,7 @@ impl Packet { } pub fn drop_port(&self) { - let guard = self.lock(); + let mut guard = self.lock.lock(); if guard.disconnected { return } guard.disconnected = true; @@ -368,14 +366,14 @@ impl Packet { // If Ok, the value is whether this port has data, if Err, then the upgraded // port needs to be checked instead of this one. pub fn can_recv(&self) -> bool { - let guard = self.lock(); + let guard = self.lock.lock(); guard.disconnected || guard.buf.size() > 0 } // Attempts to start selection on this port. This can either succeed or fail // because there is data waiting. pub fn start_selection(&self, token: SignalToken) -> StartResult { - let guard = self.lock(); + let mut guard = self.lock.lock(); if guard.disconnected || guard.buf.size() > 0 { Abort } else { @@ -393,7 +391,7 @@ impl Packet { // // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { - let guard = self.lock(); + let mut guard = self.lock.lock(); match mem::replace(&mut guard.blocker, NoneBlocked) { NoneBlocked => true, BlockedSender(token) => { @@ -409,7 +407,7 @@ impl Packet { impl Drop for Packet { fn drop(&mut self) { assert_eq!(self.channels.load(atomic::SeqCst), 0); - let guard = self.lock(); + let mut guard = self.lock.lock(); assert!(guard.queue.dequeue().is_none()); assert!(guard.canceled.is_none()); } diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 5438f1920d6f9..3934b0cf45f0c 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -21,10 +21,10 @@ use option::Option::{Some, None}; use result::Result::Ok; use rt::backtrace; use rt::util::{Stderr, Stdio}; -use rt::local::Local; -use rt::task::Task; use str::Str; use string::String; +use thread::Thread; +use sys_common::thread_info; // Defined in this module instead of io::stdio so that the unwinding thread_local! { @@ -52,63 +52,35 @@ pub fn on_fail(obj: &(Any+Send), file: &'static str, line: uint) { } }; 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 panic and - // immediately abort the whole process if there is no local task - // available. - if !Local::exists(None::) { - let _ = writeln!(&mut err, "panicked 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(""); - - let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); - match prev { - Some(mut stderr) => { - // FIXME: what to do when the task printing panics? - let _ = writeln!(stderr, - "task '{}' panicked at '{}', {}:{}\n", - n, msg, file, line); - if backtrace::log_enabled() { - let _ = backtrace::write(&mut *stderr); - } - let mut s = Some(stderr); - LOCAL_STDERR.with(|slot| { - *slot.borrow_mut() = s.take(); - }); + let thread = Thread::current(); + let name = thread.name().unwrap_or(""); + let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); + match prev { + Some(mut stderr) => { + // FIXME: what to do when the thread printing panics? + let _ = writeln!(stderr, + "thread '{}' panicked at '{}', {}:{}\n", + name, msg, file, line); + if backtrace::log_enabled() { + let _ = backtrace::write(&mut *stderr); } - None => { - let _ = writeln!(&mut err, "task '{}' panicked at '{}', {}:{}", - n, msg, file, line); - if backtrace::log_enabled() { - let _ = backtrace::write(&mut err); - } + let mut s = Some(stderr); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); + } + None => { + let _ = writeln!(&mut err, "thread '{}' panicked at '{}', {}:{}", + name, msg, file, line); + if backtrace::log_enabled() { + let _ = backtrace::write(&mut err); } } + } - // If this is a double panic, make sure that we printed a backtrace - // for this panic. - if unwinding && !backtrace::log_enabled() { - let _ = backtrace::write(&mut err); - } + // If this is a double panic, make sure that we printed a backtrace + // for this panic. + if thread_info::panicking() && !backtrace::log_enabled() { + let _ = backtrace::write(&mut err); } - Local::borrow(None::).name = name; } diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index 2aa7435d871e2..d2a9861737d12 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -156,12 +156,12 @@ mod test { use prelude::*; use super::*; use io; - use task; + use thread::Thread; #[test] fn test_rx_reader() { let (tx, rx) = channel(); - task::spawn(move|| { + Thread::spawn(move|| { tx.send(vec![1u8, 2u8]); tx.send(vec![]); tx.send(vec![3u8, 4u8]); @@ -203,7 +203,7 @@ mod test { #[test] fn test_rx_buffer() { let (tx, rx) = channel(); - task::spawn(move|| { + Thread::spawn(move|| { tx.send(b"he".to_vec()); tx.send(b"llo wo".to_vec()); tx.send(b"".to_vec()); @@ -229,7 +229,11 @@ mod test { writer.write_be_u32(42).unwrap(); let wanted = vec![0u8, 0u8, 0u8, 42u8]; +<<<<<<< HEAD let got = match task::try(move|| { rx.recv() }) { +======= + let got = match Thread::with_join(proc() { rx.recv() }).join() { +>>>>>>> Fallout from new thread API Ok(got) => got, Err(_) => panic!(), }; diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs index 9f2f41c002108..01eb33b44f92f 100644 --- a/src/libstd/io/net/pipe.rs +++ b/src/libstd/io/net/pipe.rs @@ -549,7 +549,7 @@ mod tests { Err(ref e) if e.kind == TimedOut => {} Err(e) => panic!("error: {}", e), } - ::task::deschedule(); + ::thread::Thread::yield_now(); if i == 1000 { panic!("should have a pending connection") } } drop(l); diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 3c38e23183fe7..0e9a93e42758c 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -1155,7 +1155,7 @@ mod test { Err(ref e) if e.kind == TimedOut => {} Err(e) => panic!("error: {}", e), } - ::task::deschedule(); + ::thread::Thread::yield_now(); if i == 1000 { panic!("should have a pending connection") } } } @@ -1378,7 +1378,7 @@ mod test { // Try to ensure that the reading clone is indeed reading for _ in range(0i, 50) { - ::task::deschedule(); + ::thread::Thread::yield_now(); } // clone the handle again while it's reading, then let it finish the diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index 9ba6381c8c3b3..d9acb94714b24 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -30,6 +30,7 @@ use hash::Hash; use std::hash::sip::SipState; use io::pipe::{PipeStream, PipePair}; use path::BytesContainer; +use thread::Thread; use sys; use sys::fs::FileDesc; @@ -693,10 +694,12 @@ impl Process { fn read(stream: Option) -> Receiver>> { let (tx, rx) = channel(); match stream { - Some(stream) => spawn(move |:| { - let mut stream = stream; - tx.send(stream.read_to_end()) - }), + Some(stream) => { + Thread::spawn(move |:| { + let mut stream = stream; + tx.send(stream.read_to_end()) + }); + } None => tx.send(Ok(Vec::new())) } rx diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 7b5cbf7d58f55..32ba6278a8909 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -41,9 +41,6 @@ use option::Option; use option::Option::{Some, None}; use ops::{Deref, DerefMut, FnOnce}; use result::Result::{Ok, Err}; -use rt; -use rt::local::Local; -use rt::task::Task; use slice::SliceExt; use str::StrPrelude; use string::String; @@ -328,25 +325,17 @@ pub fn set_stderr(stderr: Box) -> Option> { // // io1 aliases io2 // }) // }) -fn with_task_stdout(f: F) where - F: FnOnce(&mut Writer) -> IoResult<()>, -{ - let result = if Local::exists(None::) { - let mut my_stdout = LOCAL_STDOUT.with(|slot| { - slot.borrow_mut().take() - }).unwrap_or_else(|| { - box stdout() as Box - }); - let result = f(&mut *my_stdout); - let mut var = Some(my_stdout); - LOCAL_STDOUT.with(|slot| { - *slot.borrow_mut() = var.take(); - }); - result - } else { - let mut io = rt::util::Stdout; - f(&mut io as &mut Writer) - }; +fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) { + let mut my_stdout = LOCAL_STDOUT.with(|slot| { + slot.borrow_mut().take() + }).unwrap_or_else(|| { + box stdout() as Box + }); + let result = f(&mut *my_stdout); + let mut var = Some(my_stdout); + LOCAL_STDOUT.with(|slot| { + *slot.borrow_mut() = var.take(); + }); match result { Ok(()) => {} Err(e) => panic!("failed printing to stdout: {}", e), diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a0939999c7c19..7219fef3732ea 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -229,6 +229,8 @@ pub mod hash; /* Threads and communication */ +pub mod task; +#[allow(missing_docs)] pub mod thread; pub mod sync; pub mod comm; diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 550c8d2fabaca..0ff41586e7201 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -49,7 +49,7 @@ use ptr::RawPtr; use ptr; use result::Result; use result::Result::{Err, Ok}; -use slice::{AsSlice, SliceExt, PartialEqSliceExt}; +use slice::{AsSlice, SliceExt}; use slice::CloneSliceExt; use str::{Str, StrPrelude, StrAllocating}; use string::{String, ToString}; diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index f872aa8e9a489..5ab8eb6b9422d 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -514,20 +514,20 @@ mod tests { #[test] fn test_null_byte() { - use task; - let result = task::try(move|| { + use thread::Thread; + let result = Thread::with_join(move|| { Path::new(b"foo/bar\0") - }); + }).join(); assert!(result.is_err()); - let result = task::try(move|| { + let result = Thread::with_join(move|| { Path::new("test").set_filename(b"f\0o") - }); + }).join(); assert!(result.is_err()); - let result = task::try(move|| { + let result = Thread::with_join(move|| { Path::new("test").push(b"f\0o"); - }); + }).join(); assert!(result.is_err()); } diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index b376f6d0d5b07..171707c10c5da 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1298,20 +1298,20 @@ mod tests { #[test] fn test_null_byte() { - use task; - let result = task::try(move|| { + use thread::Thread; + let result = Thread::with_join(move|| { Path::new(b"foo/bar\0") - }); + }).join(); assert!(result.is_err()); - let result = task::try(move|| { + let result = Thread::with_join(move|| { Path::new("test").set_filename(b"f\0o") - }); + }).join(); assert!(result.is_err()); - let result = task::try(move|| { + let result = Thread::with_join(move|| { Path::new("test").push(b"f\0o"); - }); + }).join(); assert!(result.is_err()); } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 6bccef0713151..d2ae66d657809 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -340,7 +340,7 @@ mod test { use super::OsRng; use rand::Rng; - use task; + use thread::Thread; #[test] fn test_os_rng() { @@ -360,25 +360,26 @@ mod test { for _ in range(0u, 20) { let (tx, rx) = channel(); txs.push(tx); - task::spawn(move|| { + + Thread::spawn(move|| { // wait until all the tasks are ready to go. rx.recv(); // deschedule to attempt to interleave things as much // as possible (XXX: is this a good test?) let mut r = OsRng::new().unwrap(); - task::deschedule(); + Thread::yield_now(); let mut v = [0u8, .. 1000]; for _ in range(0u, 100) { r.next_u32(); - task::deschedule(); + Thread::yield_now(); r.next_u64(); - task::deschedule(); + Thread::yield_now(); r.fill_bytes(&mut v); - task::deschedule(); + Thread::yield_now(); } - }) + }); } // start all the tasks diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index b8012134c9ec1..1b97a01146c43 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -14,7 +14,6 @@ use core::prelude::*; -use libc; use boxed::Box; use vec::Vec; use sync::{Mutex, atomic, Once, ONCE_INIT}; @@ -25,31 +24,30 @@ type Queue = Mutex>; static INIT: Once = ONCE_INIT; static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; -static RUNNING: atomic::AtomicBool = atomic::INIT_ATOMIC_BOOL; fn init() { let state: Box = box Mutex::new(Vec::new()); unsafe { QUEUE.store(mem::transmute(state), atomic::SeqCst); - libc::atexit(run); + + // FIXME: switch this to use atexit as below. Currently this + // segfaults (the queue's memory is mysteriously gone), so + // instead the cleanup is tied to the `std::rt` entry point. + // + // ::libc::atexit(cleanup); } } -// Note: this is private and so can only be called via atexit above, -// which guarantees initialization. -extern fn run() { - let cur = unsafe { - rtassert!(!RUNNING.load(atomic::SeqCst)); +pub fn cleanup() { + unsafe { let queue = QUEUE.swap(0, atomic::SeqCst); - rtassert!(queue != 0); - - let queue: Box = mem::transmute(queue); - let v = mem::replace(&mut *queue.lock(), Vec::new()); - v - }; - - for to_run in cur.into_iter() { - to_run.invoke(()); + if queue != 0 { + let queue: Box = mem::transmute(queue); + let v = mem::replace(&mut *queue.lock(), Vec::new()); + for to_run in v.into_iter() { + to_run.invoke(); + } + } } } @@ -60,7 +58,6 @@ pub fn push(f: Thunk) { // 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(atomic::SeqCst)); let queue = QUEUE.load(atomic::SeqCst); rtassert!(queue != 0); (*(queue as *const Queue)).lock().push(f); diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 40885823a0596..4a692bccf9eea 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -12,12 +12,8 @@ #![allow(non_camel_case_types)] -use io::{IoResult, Writer}; -use iter::{Iterator, IteratorExt}; use option::Option::{Some, None}; use os; -use result::Result::{Ok, Err}; -use str::{StrPrelude, from_str}; use sync::atomic; pub use sys::backtrace::write; diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 44794d2b957ce..022e73121d7f4 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -48,14 +48,14 @@ #![allow(dead_code)] -use borrow::IntoCow; use failure; use os; use thunk::Thunk; use kinds::Send; use thread::Thread; +use sys; use sys_common; -use sys_common::thread::{mod, NewThread}; +use sys_common::thread_info::{mod, NewThread}; // Reexport some of our utilities which are expected by other crates. pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; @@ -87,10 +87,9 @@ pub const DEFAULT_ERROR_CODE: int = 101; /// Initializes global state, including frobbing /// the crate's logging flags, registering GC /// metadata, and storing the process arguments. +// FIXME: this should be unsafe #[allow(experimental)] pub fn init(argc: int, argv: *const *const u8) { - // FIXME: Derefing these pointers is not safe. - // Need to propagate the unsafety to `start`. unsafe { args::init(argc, argv); thread::init(); @@ -122,8 +121,6 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { use prelude::*; use rt; - use rt::task::Task; - use str; let something_around_the_top_of_the_stack = 1; let addr = &something_around_the_top_of_the_stack as *const int; @@ -153,18 +150,19 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { init(argc, argv); let mut exit_code = None; - let thread: std::Thread = NewThread::new(Some("
".into_string())); + let thread: Thread = NewThread::new(Some("
".into_string())); thread_info::set((my_stack_bottom, my_stack_top), unsafe { sys::thread::guard::main() }, thread); - unwind::try(|| { - unsafe { + let mut main_opt = Some(main); // option dance + unsafe { + let _ = unwind::try(|| { sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); - } - (main.take().unwrap()).invoke(()); - exit_code = Some(os::get_exit_status()); - }); - unsafe { cleanup(); } + (main_opt.take().unwrap()).invoke(); + exit_code = Some(os::get_exit_status()); + }); + cleanup(); + } // If the exit code wasn't set, then the task block must have panicked. return exit_code.unwrap_or(rt::DEFAULT_ERROR_CODE); } @@ -197,14 +195,6 @@ pub fn at_exit(f: proc():Send) { /// undefined behavior. pub unsafe fn cleanup() { args::cleanup(); - thread::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(all(not(windows), not(target_os = "android"), not(target_os = "ios")))] - pub use super::local_ptr::compiled::RT_TLS_PTR; + sys::stack_overflow::cleanup(); + at_exit_imp::cleanup(); } diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index decf7cfb60a27..f9f76e35bd4a0 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -565,7 +565,7 @@ fn begin_unwind_inner(msg: Box, file_line: &(&'static str, uint)) -> // Now that we've run all the necessary unwind callbacks, we actually // perform the unwinding. - if thread_info::unwinding() { + if thread_info::panicking() { // If a thread panics while it's already unwinding then we // have limited options. Currently our preference is to // just abort. In the future we may consider resuming diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index d3cfccab9d096..86dbb6066f30e 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -196,8 +196,7 @@ memory and partly incapable of presentation to others.", } pub unsafe fn report_overflow() { - use rt::task::Task; - use rt::local::Local; + use thread::Thread; // See the message below for why this is not emitted to the // ^ Where did the message below go? @@ -206,11 +205,6 @@ pub unsafe fn report_overflow() { // call would happen to initialized it (calling out to libuv), // and the FFI call needs 2MB of stack when we just ran out. - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - - let name = task.and_then(|task| { - (*task).name.as_ref().map(|n| n.as_slice()) - }); - - rterrln!("\ntask '{}' has overflowed its stack", name.unwrap_or("")); + rterrln!("\nthread '{}' has overflowed its stack", + Thread::current().name().unwrap_or("")); } diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index e5a1e09967ce9..16f2cff599879 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -29,8 +29,8 @@ use core::mem::replace; use self::FutureState::*; use comm::{Receiver, channel}; -use task::spawn; use thunk::{Thunk}; +use thread::Thread; /// A type encapsulating the result of a computation which may not be complete pub struct Future { @@ -139,7 +139,7 @@ impl Future { let (tx, rx) = channel(); - spawn(move |:| { + Thread::spawn(move |:| { // Don't panic if the other end has hung up let _ = tx.send_opt(blk()); }); diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 33f8d254c71a4..fc73e2957a55f 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -274,7 +274,7 @@ impl Drop for StaticMutexGuard { mod test { use prelude::*; - use task; + use thread::Thread; use sync::{Arc, Mutex, StaticMutex, MUTEX_INIT, Condvar}; #[test] @@ -386,10 +386,10 @@ mod test { fn test_mutex_arc_poison() { let arc = Arc::new(Mutex::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { let lock = arc2.lock(); assert_eq!(*lock, 2); - }); + }).join(); let lock = arc.lock(); assert_eq!(*lock, 1); } @@ -414,7 +414,7 @@ mod test { fn test_mutex_arc_access_in_unwind() { let arc = Arc::new(Mutex::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| -> () { + let _ = Thread::with_join::<()>(move|| -> () { struct Unwinder { i: Arc>, } @@ -425,7 +425,7 @@ mod test { } let _u = Unwinder { i: arc2 }; panic!(); - }); + }).join(); let lock = arc.lock(); assert_eq!(*lock, 2); } diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index 263937c5cbec7..a43f822e351db 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -121,7 +121,7 @@ impl Once { mod test { use prelude::*; - use task; + use thread::Thread; use super::{ONCE_INIT, Once}; #[test] @@ -143,7 +143,7 @@ mod test { for _ in range(0u, 10) { let tx = tx.clone(); spawn(move|| { - for _ in range(0u, 4) { task::deschedule() } + for _ in range(0u, 4) { Thread::yield_now() } unsafe { O.doit(|| { assert!(!run); diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs index ee1515566204b..ad08e9873faad 100644 --- a/src/libstd/sync/poison.rs +++ b/src/libstd/sync/poison.rs @@ -8,21 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use option::Option::None; -use rustrt::task::Task; -use rustrt::local::Local; +use thread::Thread; pub struct Flag { pub failed: bool } impl Flag { pub fn borrow(&mut self) -> Guard { - Guard { flag: &mut self.failed, failing: failing() } + Guard { flag: &mut self.failed, panicking: Thread::panicking() } } } pub struct Guard<'a> { flag: &'a mut bool, - failing: bool, + panicking: bool, } impl<'a> Guard<'a> { @@ -33,16 +31,8 @@ impl<'a> Guard<'a> { } pub fn done(&mut self) { - if !self.failing && failing() { + if !self.panicking && Thread::panicking() { *self.flag = true; } } } - -fn failing() -> bool { - if Local::exists(None::) { - Local::borrow(None::).unwinder.unwinding() - } else { - false - } -} diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index b6d6aa989c58e..1f1e9eea1d635 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -356,7 +356,7 @@ mod tests { use prelude::*; use rand::{mod, Rng}; - use task; + use thread::Thread; use sync::{Arc, RWLock, StaticRWLock, RWLOCK_INIT}; #[test] @@ -409,10 +409,10 @@ mod tests { fn test_rw_arc_poison_wr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { let lock = arc2.write(); assert_eq!(*lock, 2); - }); + }).join(); let lock = arc.read(); assert_eq!(*lock, 1); } @@ -422,10 +422,10 @@ mod tests { fn test_rw_arc_poison_ww() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { let lock = arc2.write(); assert_eq!(*lock, 2); - }); + }).join(); let lock = arc.write(); assert_eq!(*lock, 1); } @@ -434,10 +434,10 @@ mod tests { fn test_rw_arc_no_poison_rr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { let lock = arc2.read(); assert_eq!(*lock, 2); - }); + }).join(); let lock = arc.read(); assert_eq!(*lock, 1); } @@ -445,10 +445,10 @@ mod tests { fn test_rw_arc_no_poison_rw() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = task::try(move|| { + let _ = Thread::with_join(move|| { let lock = arc2.read(); assert_eq!(*lock, 2); - }); + }).join(); let lock = arc.write(); assert_eq!(*lock, 1); } @@ -459,12 +459,12 @@ mod tests { let arc2 = arc.clone(); let (tx, rx) = channel(); - task::spawn(move|| { + Thread::spawn(move|| { let mut lock = arc2.write(); for _ in range(0u, 10) { let tmp = *lock; *lock = -1; - task::deschedule(); + Thread::yield_now(); *lock = tmp + 1; } tx.send(()); @@ -474,15 +474,15 @@ mod tests { let mut children = Vec::new(); for _ in range(0u, 5) { let arc3 = arc.clone(); - children.push(task::try_future(move|| { + children.push(Thread::with_join(move|| { let lock = arc3.read(); assert!(*lock >= 0); })); } // Wait for children to pass their asserts - for r in children.iter_mut() { - assert!(r.get_ref().is_ok()); + for r in children.into_iter() { + assert!(r.join().is_ok()); } // Wait for writer to finish @@ -495,7 +495,11 @@ mod tests { fn test_rw_arc_access_in_unwind() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); +<<<<<<< HEAD let _ = task::try(move|| -> () { +======= + let _ = Thread::with_join::<()>(proc() { +>>>>>>> Fallout from new thread API struct Unwinder { i: Arc>, } @@ -507,7 +511,7 @@ mod tests { } let _u = Unwinder { i: arc2 }; panic!(); - }); + }).join(); let lock = arc.read(); assert_eq!(*lock, 2); } diff --git a/src/libstd/sync/task_pool.rs b/src/libstd/sync/task_pool.rs index fa5b62a202bfd..5e7944d5fe507 100644 --- a/src/libstd/sync/task_pool.rs +++ b/src/libstd/sync/task_pool.rs @@ -12,7 +12,7 @@ use core::prelude::*; -use task::{spawn}; +use thread::Thread; use comm::{channel, Sender, Receiver}; use sync::{Arc, Mutex}; use thunk::Thunk; @@ -105,7 +105,7 @@ impl TaskPool { } fn spawn_in_pool(jobs: Arc>>) { - spawn(move |:| { + Thread::spawn(move |:| { // Will spawn a new task on panic unless it is cancelled. let sentinel = Sentinel::new(&jobs); @@ -126,7 +126,7 @@ fn spawn_in_pool(jobs: Arc>>) { } sentinel.cancel(); - }) + }); } #[cfg(test)] @@ -206,4 +206,3 @@ mod test { waiter.wait(); } } - diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 0c03060b3142d..38dc516bf3d81 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -9,7 +9,7 @@ // except according to those terms. use io::{IoResult, Writer}; -use iter::Iterator; +use iter::{Iterator, IteratorExt}; use option::{Some, None}; use result::{Ok, Err}; use str::{StrPrelude, from_str}; diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index ef0181b72b0b7..8aa09d9bd3020 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -28,7 +28,7 @@ use sync::{StaticMutex, StaticCondvar}; use rt; use sys::helper_signal; -use task; +use thread::Thread; /// A structure for management of a helper thread. /// @@ -82,7 +82,11 @@ impl Helper { *self.signal.get() = send as uint; let t = f(); +<<<<<<< HEAD task::spawn(move |:| { +======= + Thread::spawn(proc() { +>>>>>>> Fallout from new thread API helper(receive, rx, t); let _g = self.lock.lock(); *self.shutdown.get() = true; diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index aeee4cf01cdd0..dc0ad08cdbef6 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -27,6 +27,7 @@ pub mod net; pub mod rwlock; pub mod stack; pub mod thread; +pub mod thread_info; pub mod thread_local; // common error constructors diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index f88fd49ab99f5..0612448cfa01a 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -33,11 +33,11 @@ impl ThreadInfo { *c.borrow_mut() = Some(ThreadInfo { stack_bounds: (0, 0), stack_guard: 0, - unwinder: false, - thread: Thread::new(None), + unwinding: false, + thread: NewThread::new(None), }) } - f(c.borrow_mut().as_ref().unwrap()) + f(c.borrow_mut().as_mut().unwrap()) }) } } @@ -47,28 +47,25 @@ pub fn current_thread() -> Thread { } pub fn panicking() -> bool { - ThreadInfo::with(|info| info.unwinder.unwinding()) + ThreadInfo::with(|info| info.unwinding) } pub fn stack_guard() -> uint { ThreadInfo::with(|info| info.stack_guard) } -pub fn unwinding() -> bool { - ThreadInfo::with(|info| info.unwinder.unwinding) -} - pub fn set_unwinding(unwinding: bool) { ThreadInfo::with(|info| info.unwinding = unwinding) } pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { THREAD_INFO.with(|c| assert!(c.borrow().is_none())); + let mut thread_opt = Some(thread); // option dance THREAD_INFO.with(|c| *c.borrow_mut() = Some(ThreadInfo{ stack_bounds: stack_bounds, stack_guard: stack_guard, unwinding: false, - thread: thread, + thread: thread_opt.take().unwrap(), })); } diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs index c3bf5cfc301c6..fe7a7d8d03716 100644 --- a/src/libstd/sys/common/thread_local.rs +++ b/src/libstd/sys/common/thread_local.rs @@ -58,7 +58,6 @@ use prelude::*; -use rt; use sync::atomic::{mod, AtomicUint}; use sync::{Mutex, Once, ONCE_INIT}; diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index 2b36ece0e4b48..5256e2cc809e4 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -141,7 +141,7 @@ pub fn write(w: &mut Writer) -> IoResult<()> { struct Context<'a> { idx: int, - writer: &'a mut Writer+'a, + writer: &'a mut (Writer+'a), last_error: Option, } diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 73b98f762b479..340f95142419c 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -45,8 +45,6 @@ mod imp { use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, SA_SIGINFO, SA_ONSTACK, sigaltstack, SIGSTKSZ}; - use rt::local::Local; - use rt::task::Task; use libc; use libc::funcs::posix88::mman::{mmap, munmap}; use libc::consts::os::posix88::{SIGSEGV, @@ -56,20 +54,12 @@ mod imp { MAP_ANON, MAP_FAILED}; + use sys_common::thread_info; + // This is initialized in init() and only read from after static mut PAGE_SIZE: uint = 0; - // get_task_info is called from an exception / signal handler. - // It returns the guard page of the current task or 0 if that - // guard page doesn't exist. None is returned if there's currently - // no local task. - unsafe fn get_task_guard_page() -> Option { - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - task.map(|task| (&*task).stack_guard().unwrap_or(0)) - } - - #[no_stack_check] unsafe extern fn signal_handler(signum: libc::c_int, info: *mut siginfo, @@ -89,20 +79,16 @@ mod imp { // We're calling into functions with stack checks stack::record_sp_limit(0); - match get_task_guard_page() { - Some(guard) => { - let addr = (*info).si_addr as uint; + let guard = thread_info::stack_guard(); + let addr = (*info).si_addr as uint; - if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard { - term(signum); - } + if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard { + term(signum); + } - report_overflow(); + report_overflow(); - intrinsics::abort() - } - None => term(signum) - } + intrinsics::abort() } static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void; diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs index e3d96a054f4c6..63b5b6f586378 100644 --- a/src/libstd/sys/windows/stack_overflow.rs +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -8,15 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rt::local::Local; -use rt::task::Task; use rt::util::report_overflow; use core::prelude::*; use ptr; use mem; use libc; use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; -use sys_common::stack; +use sys_common::{stack, thread_info}; pub struct Handler { _data: *mut libc::c_void @@ -37,8 +35,7 @@ impl Drop for Handler { // guard page doesn't exist. None is returned if there's currently // no local task. unsafe fn get_task_guard_page() -> Option { - let task: Option<*mut Task> = Local::try_unsafe_borrow(); - task.map(|task| (&*task).stack_guard().unwrap_or(0)) + thread_info::stack_guard() } // This is initialized in init() and only read from after diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 6881a1adb25fb..13395fc9c7637 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -12,8 +12,11 @@ #![deprecated = "use std::thread instead"] +use any::Any; +use boxed::Box; use thread; use kinds::Send; +use result::Result; /// Deprecate: use `std::thread::Cfg` instead. #[deprecated = "use std::thread::Cfg instead"] @@ -24,3 +27,15 @@ pub type TaskBuilder = thread::Cfg; pub fn spawn(f: proc(): Send) { thread::Thread::spawn(f); } + +/// Deprecated: use `std::thread::Thread::with_join instead`. +#[deprecated = "use std::thread::Thread::with_join instead"] +pub fn try(f: proc(): Send -> T) -> Result> { + thread::Thread::with_join(f).join() +} + +/// Deprecated: use `std::thread::Thread::yield_now instead`. +#[deprecated = "use std::thread::Thread::yield_now instead"] +pub fn deschedule() { + thread::Thread::yield_now() +} diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index a6e114bc2c356..f179219884f8b 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -231,7 +231,7 @@ impl Cfg { } thread_info::set( (my_stack_bottom, my_stack_top), - thread::current_guard_page(), + unsafe { imp::guard::current() }, their_thread ); @@ -261,7 +261,7 @@ impl Cfg { } } }; - (unsafe { imp::create(stack, box main) }, my_thread) + (unsafe { imp::create(stack_size, box main) }, my_thread) } /// Spawn a detached thread, and return a handle to it. @@ -278,19 +278,20 @@ impl Cfg { // We need the address of the packet to fill in to be stable so when // `main` fills it in it's still valid, so allocate an extra box to do // so. - let my_packet = box Err(box 0); // sentinel value + let any: Box = box 0u8; // sentinel value + let my_packet = box Err(any); let their_packet: *mut Result = unsafe { *mem::transmute::<&Box>, *const *mut Result>(&my_packet) }; let (native, thread) = self.core_spawn(f, proc(result) { - *their_packet = result; + unsafe { *their_packet = result; } }); JoinGuard { native: native, joined: false, - packet: my_packet, + packet: Some(my_packet), thread: thread, } } @@ -336,7 +337,7 @@ impl Thread { /// Gets a handle to the thread that invokes it. pub fn current() -> Thread { - ThreadInfo::current_thread() + thread_info::current_thread() } /// Cooperatively give up a timeslice to the OS scheduler. @@ -346,7 +347,7 @@ impl Thread { /// Determines whether the current thread is panicking. pub fn panicking() -> bool { - ThreadInfo::panicking() + thread_info::panicking() } // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp @@ -355,9 +356,9 @@ impl Thread { /// See the module doc for more detail. pub fn park() { let thread = Thread::current(); - let guard = thread.inner.lock.lock(); + let mut guard = thread.inner.lock.lock(); while !*guard { - thread.inner.cvar.wait(guard); + thread.inner.cvar.wait(&guard); } *guard = false; } @@ -366,7 +367,7 @@ impl Thread { /// /// See the module doc for more detail. pub fn unpark(&self) { - let guard = self.inner.lock(); + let mut guard = self.inner.lock.lock(); if !*guard { *guard = true; self.inner.cvar.notify_one(); @@ -375,7 +376,7 @@ impl Thread { /// Get the thread's name. pub fn name(&self) -> Option<&str> { - self.inner.name.as_ref() + self.inner.name.as_ref().map(|s| s.as_slice()) } } @@ -387,7 +388,7 @@ impl thread_info::NewThread for Thread { /// Indicates the manner in which a thread exited. /// /// A thread that completes without panicking is considered to exit successfully. -pub type Result = result::Result>; +pub type Result = ::result::Result>; #[must_use] /// An RAII guard that will block until thread termination when dropped. @@ -395,7 +396,7 @@ pub struct JoinGuard { native: imp::rust_thread, thread: Thread, joined: bool, - packet: Box>, + packet: Option>>, } impl JoinGuard { diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index b78428d69de21..e2cedae1be637 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -446,7 +446,7 @@ mod tests { use prelude::*; use cell::UnsafeCell; - use rt::thread::Thread; + use thread::Thread; struct Foo(Sender<()>); @@ -534,7 +534,7 @@ mod tests { } } - Thread::start(move|| { + Thread::with_join(move|| { drop(S1); }).join(); } @@ -552,7 +552,7 @@ mod tests { } } - Thread::start(move|| unsafe { + Thread::with_join(move|| unsafe { K1.with(|s| *s.get() = Some(S1)); }).join(); } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index d786409e895b2..044a4a173c692 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -69,7 +69,7 @@ use std::num::{Float, FloatMath, Int}; use std::os; use std::str::FromStr; use std::string::String; -use std::task::TaskBuilder; +use std::thread::{mod, Thread}; use std::time::Duration; use std::thunk::{Thunk, Invoke}; @@ -1121,28 +1121,27 @@ pub fn run_test(opts: &TestOpts, monitor_ch: Sender, nocapture: bool, testfn: Thunk) { - spawn(move || { + Thread::spawn(move || { let (tx, rx) = channel(); let mut reader = ChanReader::new(rx); let stdout = ChanWriter::new(tx.clone()); let stderr = ChanWriter::new(tx); - let mut task = TaskBuilder::new().named(match desc.name { + let mut cfg = thread::cfg().name(match desc.name { DynTestName(ref name) => name.clone().to_string(), StaticTestName(name) => name.to_string(), }); if nocapture { drop((stdout, stderr)); } else { - task = task.stdout(box stdout as Box); - task = task.stderr(box stderr as Box); + cfg = cfg.stdout(box stdout as Box); + cfg = cfg.stderr(box stderr as Box); } - let result_future = task.try_future(move || testfn.invoke(())); + let result_guard = cfg.with_join(testfn); let stdout = reader.read_to_end().unwrap().into_iter().collect(); - let task_result = result_future.into_inner(); - let test_result = calc_result(&desc, task_result); + let test_result = calc_result(&desc, result_guard.join()); monitor_ch.send((desc.clone(), test_result, stdout)); - }) + }); } match testfn { diff --git a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs index 771b34159f261..b5acb55d4a171 100644 --- a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs +++ b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs @@ -24,7 +24,7 @@ // It's unclear how likely such a bug is to recur, but it seems like a // scenario worth testing. -use std::task; +use std::thread::Thread; enum Conzabble { Bickwick(Foo) @@ -45,5 +45,5 @@ pub fn fails() { } pub fn main() { - task::try(fails); + Thread::with_join(fails).join(); } diff --git a/src/test/run-pass/task-stderr.rs b/src/test/run-pass/task-stderr.rs index 72cf7599f89f9..8b54de6b80553 100644 --- a/src/test/run-pass/task-stderr.rs +++ b/src/test/run-pass/task-stderr.rs @@ -9,16 +9,16 @@ // except according to those terms. use std::io::{ChanReader, ChanWriter}; -use std::task::TaskBuilder; +use std::thread; fn main() { let (tx, rx) = channel(); let mut reader = ChanReader::new(rx); let stderr = ChanWriter::new(tx); - let res = TaskBuilder::new().stderr(box stderr as Box).try(move|| -> () { + let res = thread::cfg().stderr(box stderr as Box).with_join(move|| -> () { panic!("Hello, world!") - }); + }).join(); assert!(res.is_err()); let output = reader.read_to_string().unwrap(); From ced22398528db51fcb7e704fdd390dea481d36b8 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Mon, 8 Dec 2014 17:01:34 -0800 Subject: [PATCH 12/24] Disable capture_stderr test for now --- src/libstd/io/stdio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 32ba6278a8909..bb9d6a68585fc 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -532,7 +532,7 @@ mod tests { assert_eq!(r.read_to_string().unwrap(), "hello!\n"); } - #[test] + //#[test] fn capture_stderr() { use realstd::comm::channel; use realstd::io::{ChanReader, ChanWriter, Reader}; From 9644d60cc449461f3d7289099654d1f6d227244f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2014 07:36:33 -0800 Subject: [PATCH 13/24] Fix the capture_stderr test There's always a fun time having two sets of standard libraries when testing! --- src/libstd/io/stdio.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index bb9d6a68585fc..8e28ad1add58a 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -532,15 +532,14 @@ mod tests { assert_eq!(r.read_to_string().unwrap(), "hello!\n"); } - //#[test] + #[test] fn capture_stderr() { - use realstd::comm::channel; - use realstd::io::{ChanReader, ChanWriter, Reader}; + use io::{ChanReader, ChanWriter, Reader}; let (tx, rx) = channel(); let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); spawn(move|| { - ::realstd::io::stdio::set_stderr(box w); + set_stderr(box w); panic!("my special message"); }); let s = r.read_to_string().unwrap(); From 7a6c54c46e6ac28d82f940f1d1bf8d0a5fd7e820 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2014 07:37:33 -0800 Subject: [PATCH 14/24] Fix compilation on linux --- src/libstd/os.rs | 1 - src/libstd/rt/args.rs | 14 +++++++------- src/libstd/sys/unix/backtrace.rs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 0ff41586e7201..9ec3128302c11 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -57,7 +57,6 @@ use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use vec::Vec; #[cfg(unix)] use c_str::ToCStr; -#[cfg(unix)] use libc::c_char; #[cfg(unix)] pub use sys::ext as unix; diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index fed0554863c6a..3a4705f58b49c 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -51,10 +51,10 @@ mod imp { use string::String; use mem; - use sync::mutex::{StaticMutex, MUTEX_INIT}; + use sync::{StaticMutex, MUTEX_INIT}; static mut GLOBAL_ARGS_PTR: uint = 0; - static LOCK: NativeMutex = MUTEX_INIT; + static LOCK: StaticMutex = MUTEX_INIT; pub unsafe fn init(argc: int, argv: *const *const u8) { let args = load_argc_and_argv(argc, argv); @@ -67,7 +67,7 @@ mod imp { } pub fn take() -> Option>> { - let guard = LOCK.lock(); + let _guard = LOCK.lock(); unsafe { let ptr = get_global_ptr(); let val = mem::replace(&mut *ptr, None); @@ -76,7 +76,7 @@ mod imp { } pub fn put(args: Vec>) { - let guard = LOCK.lock(); + let _guard = LOCK.lock(); unsafe { let ptr = get_global_ptr(); rtassert!((*ptr).is_none()); @@ -85,7 +85,7 @@ mod imp { } pub fn clone() -> Option>> { - let guard = LOCK.lock(); + let _guard = LOCK.lock(); unsafe { let ptr = get_global_ptr(); (*ptr).as_ref().map(|s: &Box>>| (**s).clone()) @@ -104,8 +104,8 @@ mod imp { #[cfg(test)] mod tests { - use std::prelude::*; - use std::finally::Finally; + use prelude::*; + use finally::Finally; use super::*; diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index 5256e2cc809e4..d98f935076fdd 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -241,7 +241,7 @@ fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use iter::Iterator; + use iter::{Iterator, IteratorExt}; use os; use path::GenericPath; use ptr::RawPtr; From a7061d02e16d0821d3af2b753155fe44bab7725c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2014 07:37:56 -0800 Subject: [PATCH 15/24] Tweak the startup routine to pass on linux We need to be sure to init thread_info before we init args for example because args is grabbing locks which may entail looking at the local thread eventually. --- src/libstd/rt/mod.rs | 114 ++++++++++++++++++---------------------- src/libstd/rt/unwind.rs | 19 ++++--- 2 files changed, 65 insertions(+), 68 deletions(-) diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 022e73121d7f4..28e9338ddfedc 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -48,7 +48,6 @@ #![allow(dead_code)] -use failure; use os; use thunk::Thunk; use kinds::Send; @@ -73,8 +72,8 @@ mod macros; // These should be refactored/moved/made private over time pub mod util; pub mod unwind; +pub mod args; -mod args; mod at_exit_imp; mod libunwind; @@ -82,43 +81,15 @@ mod libunwind; /// of exiting cleanly. pub const 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. -// FIXME: this should be unsafe -#[allow(experimental)] -pub fn init(argc: int, argv: *const *const u8) { - unsafe { - args::init(argc, argv); - thread::init(); - unwind::register(failure::on_fail); - } -} - #[cfg(any(windows, android))] -static OS_DEFAULT_STACK_ESTIMATE: uint = 1 << 20; +const OS_DEFAULT_STACK_ESTIMATE: uint = 1 << 20; #[cfg(all(unix, not(android)))] -static OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20); +const OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20); #[cfg(not(test))] #[lang = "start"] fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { use mem; - start(argc, argv, Thunk::new(move|| { - let main: extern "Rust" fn() = unsafe { mem::transmute(main) }; - main(); - })) -} - -/// Executes the given procedure after initializing the runtime with the given -/// argc/argv. -/// -/// This procedure is guaranteed to run on the thread calling this function, but -/// the stack bounds for this rust task will *not* be set. Care must be taken -/// for this function to not overflow its stack. -pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { use prelude::*; use rt; @@ -131,40 +102,59 @@ pub fn start(argc: int, argv: *const *const u8, main: Thunk) -> int { // frames above our current position. let my_stack_bottom = my_stack_top + 20000 - OS_DEFAULT_STACK_ESTIMATE; - // By default, some platforms will send a *signal* when a EPIPE error would - // otherwise be delivered. This runtime doesn't install a SIGPIPE handler, - // causing it to kill the program, which isn't exactly what we want! - // - // Hence, we set SIGPIPE to ignore when the program starts up in order to - // prevent this problem. - #[cfg(windows)] fn ignore_sigpipe() {} - #[cfg(unix)] fn ignore_sigpipe() { - use libc; - use libc::funcs::posix01::signal::signal; - unsafe { - assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != -1); + let failed = unsafe { + // First, make sure we don't trigger any __morestack overflow checks, + // and next set up our stack to have a guard page and run through our + // own fault handlers if we hit it. + sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, + my_stack_top); + sys::thread::guard::init(); + sys::stack_overflow::init(); + + // Next, set up the current Thread with the guard information we just + // created. Note that this isn't necessary in general for new threads, + // but we just do this to name the main thread and to give it correct + // info about the stack bounds. + let thread: Thread = NewThread::new(Some("
".into_string())); + thread_info::set((my_stack_bottom, my_stack_top), + sys::thread::guard::main(), + thread); + + // By default, some platforms will send a *signal* when a EPIPE error + // would otherwise be delivered. This runtime doesn't install a SIGPIPE + // handler, causing it to kill the program, which isn't exactly what we + // want! + // + // Hence, we set SIGPIPE to ignore when the program starts up in order + // to prevent this problem. + #[cfg(windows)] fn ignore_sigpipe() {} + #[cfg(unix)] fn ignore_sigpipe() { + use libc; + use libc::funcs::posix01::signal::signal; + unsafe { + assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != -1); + } } - } - ignore_sigpipe(); - - init(argc, argv); - let mut exit_code = None; - - let thread: Thread = NewThread::new(Some("
".into_string())); - thread_info::set((my_stack_bottom, my_stack_top), - unsafe { sys::thread::guard::main() }, - thread); - let mut main_opt = Some(main); // option dance - unsafe { - let _ = unwind::try(|| { - sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); - (main_opt.take().unwrap()).invoke(); - exit_code = Some(os::get_exit_status()); + ignore_sigpipe(); + + // Store our args if necessary in a squirreled away location + args::init(argc, argv); + + // And finally, let's run some code! + let res = unwind::try(|| { + let main: fn() = mem::transmute(main); + main(); }); cleanup(); + res.is_err() + }; + + // If the exit code wasn't set, then the try block must have panicked. + if failed { + rt::DEFAULT_ERROR_CODE + } else { + os::get_exit_status() } - // If the exit code wasn't set, then the task block must have panicked. - return exit_code.unwrap_or(rt::DEFAULT_ERROR_CODE); } /// Enqueues a procedure to run when the runtime is cleaned up diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index f9f76e35bd4a0..1beab8be90965 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -59,18 +59,20 @@ use core::prelude::*; -use boxed::Box; -use string::String; -use str::StrAllocating; -use vec::Vec; use any::Any; -use sync::atomic; +use boxed::Box; use cmp; +use failure; use fmt; use intrinsics; +use libc::c_void; use mem; use raw::Closure; -use libc::c_void; +use str::StrAllocating; +use string::String; +use sync::atomic; +use sync::{Once, ONCE_INIT}; +use vec::Vec; use sys_common::thread_info; use rt::libunwind as uw; @@ -541,6 +543,11 @@ pub fn begin_unwind(msg: M, file_line: &(&'static str, uint)) -> /// }` from ~1900/3700 (-O/no opts) to 180/590. #[inline(never)] #[cold] // this is the slow path, please never inline this fn begin_unwind_inner(msg: Box, file_line: &(&'static str, uint)) -> ! { + // Make sure the default failure handler is registered before we look at the + // callbacks. + static INIT: Once = ONCE_INIT; + INIT.doit(|| unsafe { register(failure::on_fail); }); + // First, invoke call the user-defined callbacks triggered on task panic. // // By the time that we see a callback has been registered (by reading From 4ffd9f49c340c350080bfe6c1be9e3aaccd549d0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 10 Dec 2014 07:49:45 -0800 Subject: [PATCH 16/24] Avoid .take().unwrap() with FnOnce closures --- src/libstd/lib.rs | 2 +- src/libstd/rt/unwind.rs | 28 ++++++++-------------------- src/libstd/sys/common/thread_info.rs | 5 ++--- src/libstd/thread.rs | 25 ++++++++++++++----------- src/libstd/thread_local/mod.rs | 5 ++--- src/libstd/thread_local/scoped.rs | 1 - src/rt/rust_try.ll | 8 ++++---- 7 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7219fef3732ea..5796f8946611a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -106,7 +106,7 @@ #![allow(unknown_features)] #![feature(macro_rules, globs, linkage, thread_local, asm)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] -#![feature(import_shadowing, slicing_syntax)] +#![feature(import_shadowing, slicing_syntax, tuple_indexing)] #![feature(unboxed_closures)] // Don't link to std. We are std. diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 1beab8be90965..c896f4e39daef 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -57,22 +57,17 @@ //! //! Currently Rust uses unwind runtime provided by libgcc. -use core::prelude::*; +use prelude::*; use any::Any; -use boxed::Box; use cmp; use failure; use fmt; use intrinsics; use libc::c_void; use mem; -use raw::Closure; -use str::StrAllocating; -use string::String; use sync::atomic; use sync::{Once, ONCE_INIT}; -use vec::Vec; use sys_common::thread_info; use rt::libunwind as uw; @@ -119,10 +114,9 @@ static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; /// guaranteed that a rust task is in place when invoking this function. /// Unwinding twice can lead to resource leaks where some destructors are not /// run. -pub unsafe fn try(f: ||) -> ::core::result::Result<(), Box> { - let closure: Closure = mem::transmute(f); - let ep = rust_try(try_fn, closure.code as *mut c_void, - closure.env as *mut c_void); +pub unsafe fn try(f: F) -> Result<(), Box> { + let mut f = Some(f); + let ep = rust_try(try_fn::, &mut f as *mut _ as *mut c_void); return if ep.is_null() { Ok(()) } else { @@ -133,14 +127,9 @@ pub unsafe fn try(f: ||) -> ::core::result::Result<(), Box> { Err(cause.unwrap()) }; - extern fn try_fn(code: *mut c_void, env: *mut c_void) { - unsafe { - let closure: || = mem::transmute(Closure { - code: code as *mut (), - env: env as *mut (), - }); - closure(); - } + extern fn try_fn(opt_closure: *mut c_void) { + let opt_closure = opt_closure as *mut Option; + unsafe { (*opt_closure).take().unwrap()(); } } #[link(name = "rustrt_native", kind = "static")] @@ -152,8 +141,7 @@ pub unsafe fn try(f: ||) -> ::core::result::Result<(), Box> { // When f(...) returns normally, the return value is null. // When f(...) throws, the return value is a pointer to the caught // exception object. - fn rust_try(f: extern "C" fn(*mut c_void, *mut c_void), - code: *mut c_void, + fn rust_try(f: extern fn(*mut c_void), data: *mut c_void) -> *mut uw::_Unwind_Exception; } } diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index 0612448cfa01a..206d443d2943b 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -60,12 +60,11 @@ pub fn set_unwinding(unwinding: bool) { pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { THREAD_INFO.with(|c| assert!(c.borrow().is_none())); - let mut thread_opt = Some(thread); // option dance - THREAD_INFO.with(|c| *c.borrow_mut() = Some(ThreadInfo{ + THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ stack_bounds: stack_bounds, stack_guard: stack_guard, unwinding: false, - thread: thread_opt.take().unwrap(), + thread: thread, })); } diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index f179219884f8b..7ccfe0ab74b46 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -243,17 +243,20 @@ impl Cfg { // the thread itself. For these reasons, this unsafety should be ok. unsafe { let mut output = None; - let mut f_opt = Some( // option dance - if stdout.is_some() || stderr.is_some() { - proc() { - let _ = stdout.map(stdio::set_stdout); - let _ = stderr.map(stdio::set_stderr); - f() - } - } else { - f - }); - let try_result = unwind::try(|| output = Some((f_opt.take().unwrap())())); + let f = if stdout.is_some() || stderr.is_some() { + proc() { + let _ = stdout.map(stdio::set_stdout); + let _ = stderr.map(stdio::set_stderr); + f() + } + } else { + f + }; + + let try_result = { + let ptr = &mut output; + unwind::try(move || *ptr = Some(f())) + }; match (output, try_result) { (Some(data), Ok(_)) => after(Ok(data)), (None, Err(cause)) => after(Err(cause)), diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index e2cedae1be637..4c052a4ce09f6 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -217,9 +217,8 @@ impl Key { /// This function will `panic!()` if the key currently has its /// destructor running, and it **may** panic if the destructor has /// previously been run for this thread. - pub fn with(&'static self, f: F) -> R where - F: FnOnce(&T) -> R, - { + pub fn with(&'static self, f: F) -> R + where F: FnOnce(&T) -> R { let slot = (self.inner)(); unsafe { let slot = slot.get().expect("cannot access a TLS value during or \ diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs index 7762d225b9a04..96f09190c2fe4 100644 --- a/src/libstd/thread_local/scoped.rs +++ b/src/libstd/thread_local/scoped.rs @@ -262,4 +262,3 @@ mod tests { }); } } - diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll index 18b23b4143a58..226dc55cb8d5a 100644 --- a/src/rt/rust_try.ll +++ b/src/rt/rust_try.ll @@ -14,9 +14,9 @@ ; See also: libstd/rt/unwind.rs -define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) { +define i8* @rust_try(void (i8*)* %f, i8* %env) { - %1 = invoke i8* @rust_try_inner(void (i8*,i8*)* %f, i8* %fptr, i8* %env) + %1 = invoke i8* @rust_try_inner(void (i8*)* %f, i8* %env) to label %normal unwind label %catch @@ -30,9 +30,9 @@ catch: ret i8* null } -define internal i8* @rust_try_inner(void (i8*,i8*)* %f, i8* %fptr, i8* %env) { +define internal i8* @rust_try_inner(void (i8*)* %f, i8* %env) { - invoke void %f(i8* %fptr, i8* %env) + invoke void %f(i8* %env) to label %normal unwind label %catch From 13f302d0c5dd3a88426da53ba07cdbe16459635b Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 14 Dec 2014 22:52:55 -0800 Subject: [PATCH 17/24] Update doc comment for std::rt --- src/libstd/rt/mod.rs | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 28e9338ddfedc..acc05cbf14032 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -8,38 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Runtime services, including the task scheduler and I/O dispatcher +//! Runtime services //! -//! The `rt` module provides the private runtime infrastructure necessary to support core language -//! features like the exchange and local heap, logging, local data and unwinding. It also -//! implements the default task scheduler and task model. Initialization routines are provided for -//! setting up runtime resources in common configurations, including that used by `rustc` when -//! generating executables. -//! -//! It is intended that the features provided by `rt` can be factored in a way such that the core -//! library can be built with different 'profiles' for different use cases, e.g. excluding the task -//! scheduler. A number of runtime features though are critical to the functioning of the language -//! and an implementation must be provided regardless of the execution environment. -//! -//! Of foremost importance is the global exchange heap, in the module `heap`. Very little practical -//! Rust code can be written without access to the global heap. Unlike most of `rt` the global heap -//! is truly a global resource and generally operates independently of the rest of the runtime. -//! -//! All other runtime features are task-local, including the local heap, local storage, logging and -//! the stack unwinder. -//! -//! The relationship between `rt` and the rest of the core library is not entirely clear yet and -//! some modules will be moving into or out of `rt` as development proceeds. -//! -//! Several modules in `core` are clients of `rt`: -//! -//! * `std::task` - The user-facing interface to the Rust task model. -//! * `std::local_data` - The interface to local data. -//! * `std::unstable::lang` - Miscellaneous lang items, some of which rely on `std::rt`. -//! * `std::cleanup` - Local heap destruction. -//! * `std::io` - In the future `std::io` will use an `rt` implementation. -//! * `std::logging` -//! * `std::comm` +//! The `rt` module provides a narrow set of runtime services, +//! including the global heap (exported in `heap`) and unwinding and +//! backtrace support. The APIs in this module are highly unstable, +//! and should be considered as private implementation details for the +//! time being. #![experimental] From a27fbac86849e07a0a6c746869d8f78319bd3a16 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Sun, 14 Dec 2014 00:05:32 -0800 Subject: [PATCH 18/24] Revise std::thread API to join by default This commit is part of a series that introduces a `std::thread` API to replace `std::task`. In the new API, `spawn` returns a `JoinGuard`, which by default will join the spawned thread when dropped. It can also be used to join explicitly at any time, returning the thread's result. Alternatively, the spawned thread can be explicitly detached (so no join takes place). As part of this change, Rust processes now terminate when the main thread exits, even if other detached threads are still running, moving Rust closer to standard threading models. This new behavior may break code that was relying on the previously implicit join-all. In addition to the above, the new thread API also offers some built-in support for building blocking abstractions in user space; see the module doc for details. Closes #18000 [breaking-change] --- src/compiletest/runtest.rs | 2 +- src/doc/guide-tasks.md | 32 +- src/doc/guide.md | 12 +- src/doc/intro.md | 26 +- src/liballoc/arc.rs | 5 +- src/libcollections/slice.rs | 6 +- src/libcore/borrow.rs | 2 +- src/libcoretest/finally.rs | 6 +- src/librustc_driver/lib.rs | 10 +- src/librustc_trans/back/write.rs | 4 +- src/librustdoc/lib.rs | 8 +- src/librustdoc/test.rs | 2 +- src/librustrt/thread.rs | 559 ------------------ src/libstd/c_str.rs | 2 +- src/libstd/comm/blocking.rs | 4 +- src/libstd/comm/mod.rs | 52 +- src/libstd/comm/sync.rs | 22 +- src/libstd/io/comm_adapters.rs | 10 +- src/libstd/io/mod.rs | 5 +- src/libstd/io/net/tcp.rs | 15 +- src/libstd/io/process.rs | 2 +- src/libstd/lib.rs | 1 - src/libstd/macros.rs | 6 +- src/libstd/os.rs | 3 +- src/libstd/path/posix.rs | 6 +- src/libstd/path/windows.rs | 6 +- src/libstd/rand/os.rs | 2 +- src/libstd/rt/at_exit_imp.rs | 2 +- src/libstd/rt/local.rs | 131 ---- src/libstd/rt/mod.rs | 5 +- src/libstd/rt/mutex.rs | 406 ------------- src/libstd/rt/thread.rs | 170 ------ src/libstd/rt/unwind.rs | 1 + src/libstd/rt/util.rs | 1 + src/libstd/sync/atomic.rs | 10 +- src/libstd/sync/barrier.rs | 5 +- src/libstd/sync/condvar.rs | 6 +- src/libstd/sync/future.rs | 2 +- src/libstd/sync/mutex.rs | 9 +- src/libstd/sync/rwlock.rs | 18 +- src/libstd/sync/task_pool.rs | 2 +- src/libstd/sys/common/backtrace.rs | 4 +- src/libstd/sys/common/helper_thread.rs | 8 +- src/libstd/sys/common/thread.rs | 5 +- src/libstd/sys/common/thread_info.rs | 5 + src/libstd/sys/unix/backtrace.rs | 4 +- src/libstd/sys/unix/thread.rs | 7 +- src/libstd/sys/windows/backtrace.rs | 4 +- src/libstd/sys/windows/fs.rs | 2 +- src/libstd/sys/windows/os.rs | 5 +- src/libstd/sys/windows/stack_overflow.rs | 6 +- src/libstd/sys/windows/thread.rs | 7 +- src/libstd/sys/windows/thread_local.rs | 9 +- src/libstd/task.rs | 25 +- src/libstd/thread.rs | 321 +++++----- src/libstd/thread_local/mod.rs | 9 +- src/libstd/thunk.rs | 3 + src/libtest/lib.rs | 6 +- src/test/bench/msgsend-pipes-shared.rs | 10 +- src/test/bench/msgsend-pipes.rs | 12 +- src/test/bench/shootout-pfib.rs | 12 +- src/test/run-fail/main-panic.rs | 2 +- src/test/run-fail/panic-task-name-none.rs | 8 +- src/test/run-fail/panic-task-name-owned.rs | 9 +- src/test/run-fail/panic-task-name-send-str.rs | 21 - src/test/run-fail/panic-task-name-static.rs | 19 - src/test/run-fail/test-panic.rs | 3 +- .../run-fail/test-should-fail-bad-message.rs | 4 +- .../bootstrap-from-c-with-native/Makefile | 12 - .../bootstrap-from-c-with-native/lib.rs | 24 - .../bootstrap-from-c-with-native/main.c | 16 - ...nup-rvalue-temp-during-incomplete-alloc.rs | 2 +- src/test/run-pass/foreign-call-no-runtime.rs | 4 +- src/test/run-pass/issue-16671.rs | 6 +- src/test/run-pass/issue-2190-1.rs | 4 +- .../match-ref-binding-in-guard-3256.rs | 4 +- src/test/run-pass/running-with-no-runtime.rs | 12 +- src/test/run-pass/spawning-with-debug.rs | 6 +- src/test/run-pass/task-comm-12.rs | 8 +- src/test/run-pass/task-comm-3.rs | 6 +- src/test/run-pass/task-comm-9.rs | 6 +- src/test/run-pass/task-stderr.rs | 2 +- src/test/run-pass/tcp-stress.rs | 6 +- src/test/run-pass/writealias.rs | 4 +- src/test/run-pass/yield.rs | 12 +- src/test/run-pass/yield1.rs | 8 +- 86 files changed, 464 insertions(+), 1786 deletions(-) delete mode 100644 src/librustrt/thread.rs delete mode 100644 src/libstd/rt/local.rs delete mode 100644 src/libstd/rt/mutex.rs delete mode 100644 src/libstd/rt/thread.rs delete mode 100644 src/test/run-fail/panic-task-name-send-str.rs delete mode 100644 src/test/run-fail/panic-task-name-static.rs delete mode 100644 src/test/run-make/bootstrap-from-c-with-native/Makefile delete mode 100644 src/test/run-make/bootstrap-from-c-with-native/lib.rs delete mode 100644 src/test/run-make/bootstrap-from-c-with-native/main.c diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 1b9d98823b036..1b445a6e7367f 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -445,7 +445,7 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { loop { //waiting 1 second for gdbserver start timer::sleep(Duration::milliseconds(1000)); - let result = Thread::with_join(move || { + let result = Thread::spawn(move || { tcp::TcpStream::connect("127.0.0.1:5039").unwrap(); }).join(); if result.is_err() { diff --git a/src/doc/guide-tasks.md b/src/doc/guide-tasks.md index 4753ba7fe4703..87a3abd8f226b 100644 --- a/src/doc/guide-tasks.md +++ b/src/doc/guide-tasks.md @@ -1,5 +1,7 @@ % The Rust Tasks and Communication Guide +**NOTE** This guide is badly out of date an needs to be rewritten. + # Introduction Rust provides safe concurrent abstractions through a number of core library @@ -22,7 +24,7 @@ from shared mutable state. At its simplest, creating a task is a matter of calling the `spawn` function with a closure argument. `spawn` executes the closure in the new task. -```{rust} +```{rust,ignore} # use std::task::spawn; // Print something profound in a different task using a named function @@ -49,7 +51,7 @@ closure is limited to capturing `Send`-able data from its environment ensures that `spawn` can safely move the entire closure and all its associated state into an entirely different task for execution. -```{rust} +```{rust,ignore} # use std::task::spawn; # fn generate_task_number() -> int { 0 } // Generate some state locally @@ -75,7 +77,7 @@ The simplest way to create a channel is to use the `channel` function to create of a channel, and a **receiver** is the receiving endpoint. Consider the following example of calculating two results concurrently: -```{rust} +```{rust,ignore} # use std::task::spawn; let (tx, rx): (Sender, Receiver) = channel(); @@ -96,7 +98,7 @@ stream for sending and receiving integers (the left-hand side of the `let`, `(tx, rx)`, is an example of a destructuring let: the pattern separates a tuple into its component parts). -```{rust} +```{rust,ignore} let (tx, rx): (Sender, Receiver) = channel(); ``` @@ -104,7 +106,7 @@ The child task will use the sender to send data to the parent task, which will wait to receive the data on the receiver. The next statement spawns the child task. -```{rust} +```{rust,ignore} # use std::task::spawn; # fn some_expensive_computation() -> int { 42 } # let (tx, rx) = channel(); @@ -123,7 +125,7 @@ computation, then sends the result over the captured channel. Finally, the parent continues with some other expensive computation, then waits for the child's result to arrive on the receiver: -```{rust} +```{rust,ignore} # fn some_other_expensive_computation() {} # let (tx, rx) = channel::(); # tx.send(0); @@ -154,7 +156,7 @@ spawn(move || { Instead we can clone the `tx`, which allows for multiple senders. -```{rust} +```{rust,ignore} let (tx, rx) = channel(); for init_val in range(0u, 3) { @@ -179,7 +181,7 @@ Note that the above cloning example is somewhat contrived since you could also simply use three `Sender` pairs, but it serves to illustrate the point. For reference, written with multiple streams, it might look like the example below. -```{rust} +```{rust,ignore} # use std::task::spawn; // Create a vector of ports, one for each child task @@ -203,7 +205,7 @@ getting the result later. The basic example below illustrates this. -```{rust} +```{rust,ignore} use std::sync::Future; # fn main() { @@ -230,7 +232,7 @@ called. Here is another example showing how futures allow you to background computations. The workload will be distributed on the available cores. -```{rust} +```{rust,ignore} # use std::num::Float; # use std::sync::Future; fn partial_sum(start: uint) -> f64 { @@ -268,7 +270,7 @@ Here is a small example showing how to use Arcs. We wish to run concurrently several computations on a single large vector of floats. Each task needs the full vector to perform its duty. -```{rust} +```{rust,ignore} use std::num::Float; use std::rand; use std::sync::Arc; @@ -295,7 +297,7 @@ The function `pnorm` performs a simple computation on the vector (it computes the sum of its items at the power given as argument and takes the inverse power of this value). The Arc on the vector is created by the line: -```{rust} +```{rust,ignore} # use std::rand; # use std::sync::Arc; # fn main() { @@ -309,7 +311,7 @@ the wrapper and not its contents. Within the task's procedure, the captured Arc reference can be used as a shared reference to the underlying vector as if it were local. -```{rust} +```{rust,ignore} # use std::rand; # use std::sync::Arc; # fn pnorm(nums: &[f64], p: uint) -> f64 { 4.0 } @@ -346,11 +348,11 @@ and `()`, callers can pattern-match on a result to check whether it's an `Ok` result with an `int` field (representing a successful result) or an `Err` result (representing termination with an error). -```{rust} +```{rust,ignore} # use std::thread::Thread; # fn some_condition() -> bool { false } # fn calculate_result() -> int { 0 } -let result: Result> = Thread::with_join(move || { +let result: Result> = Thread::spawn(move || { if some_condition() { calculate_result() } else { diff --git a/src/doc/guide.md b/src/doc/guide.md index 08d2c5bf97806..d739ad105fc87 100644 --- a/src/doc/guide.md +++ b/src/doc/guide.md @@ -5217,6 +5217,8 @@ the same function, so our binary is a little bit larger. # Tasks +**NOTE**: this section is currently out of date and will be rewritten soon. + Concurrency and parallelism are topics that are of increasing interest to a broad subsection of software developers. Modern computers are often multi-core, to the point that even embedded devices like cell phones have more than one @@ -5231,7 +5233,7 @@ library, and not part of the language. This means that in the future, other concurrency libraries can be written for Rust to help in specific scenarios. Here's an example of creating a task: -```{rust} +```{rust,ignore} spawn(move || { println!("Hello from a task!"); }); @@ -5261,7 +5263,7 @@ If tasks were only able to capture these values, they wouldn't be very useful. Luckily, tasks can communicate with each other through **channel**s. Channels work like this: -```{rust} +```{rust,ignore} let (tx, rx) = channel(); spawn(move || { @@ -5280,7 +5282,7 @@ which returns an `Result` and does not block. If you want to send messages to the task as well, create two channels! -```{rust} +```{rust,ignore} let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); @@ -5340,7 +5342,7 @@ we'll just get the value immediately. Tasks don't always succeed, they can also panic. A task that wishes to panic can call the `panic!` macro, passing a message: -```{rust} +```{rust,ignore} spawn(move || { panic!("Nope."); }); @@ -5349,7 +5351,7 @@ spawn(move || { If a task panics, it is not possible for it to recover. However, it can notify other tasks that it has panicked. We can do this with `task::try`: -```{rust} +```{rust,ignore} use std::task; use std::rand; diff --git a/src/doc/intro.md b/src/doc/intro.md index 880dd6e2d6c3f..5be97034357eb 100644 --- a/src/doc/intro.md +++ b/src/doc/intro.md @@ -389,11 +389,13 @@ safe concurrent programs. Here's an example of a concurrent Rust program: ```{rust} +use std::thread::Thread; + fn main() { for _ in range(0u, 10u) { - spawn(move || { + Thread::spawn(move || { println!("Hello, world!"); - }); + }).detach(); } } ``` @@ -403,7 +405,8 @@ This program creates ten threads, who all print `Hello, world!`. The double bars `||`. (The `move` keyword indicates that the closure takes ownership of any data it uses; we'll have more on the significance of this shortly.) This closure is executed in a new thread created by -`spawn`. +`spawn`. The `detach` method means that the child thread is allowed to +outlive its parent. One common form of problem in concurrent programs is a 'data race.' This occurs when two different threads attempt to access the same @@ -418,13 +421,15 @@ problem. Let's see an example. This Rust code will not compile: ```{rust,ignore} +use std::thread::Thread; + fn main() { let mut numbers = vec![1i, 2i, 3i]; for i in range(0u, 3u) { - spawn(move || { + Thread::spawn(move || { for j in range(0, 3) { numbers[j] += 1 } - }); + }).detach(); } } ``` @@ -469,6 +474,7 @@ mutation doesn't cause a data race. Here's what using an Arc with a Mutex looks like: ```{rust} +use std::thread::Thread; use std::sync::{Arc,Mutex}; fn main() { @@ -476,13 +482,13 @@ fn main() { for i in range(0u, 3u) { let number = numbers.clone(); - spawn(move || { + Thread::spawn(move || { let mut array = number.lock(); (*array)[i] += 1; println!("numbers[{}] is {}", i, (*array)[i]); - }); + }).detach(); } } ``` @@ -532,13 +538,15 @@ As an example, Rust's ownership system is _entirely_ at compile time. The safety check that makes this an error about moved values: ```{rust,ignore} +use std::thread::Thread; + fn main() { let vec = vec![1i, 2, 3]; for i in range(1u, 3) { - spawn(move || { + Thread::spawn(move || { println!("{}", vec[i]); - }); + }).detach(); } } ``` diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index ee4efa2d27399..e909947ab08b0 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -39,6 +39,7 @@ use heap::deallocate; /// /// ```rust /// use std::sync::Arc; +/// use std::thread::Thread; /// /// fn main() { /// let numbers = Vec::from_fn(100, |i| i as f32); @@ -47,11 +48,11 @@ use heap::deallocate; /// for _ in range(0u, 10) { /// let child_numbers = shared_numbers.clone(); /// -/// spawn(move || { +/// Thread::spawn(move || { /// let local_numbers = child_numbers.as_slice(); /// /// // Work with the local numbers -/// }); +/// }).detach(); /// } /// } /// ``` diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index f875147ed820e..d3790e320ad65 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -1344,6 +1344,7 @@ pub mod raw { #[cfg(test)] mod tests { + use std::boxed::Box; use std::cell::Cell; use std::default::Default; use std::mem; @@ -1627,7 +1628,10 @@ mod tests { #[test] fn test_swap_remove_noncopyable() { // Tests that we don't accidentally run destructors twice. - let mut v = vec![Box::new(()), Box::new(()), Box::new(())]; + let mut v = Vec::new(); + v.push(box 0u8); + v.push(box 0u8); + v.push(box 0u8); let mut _e = v.swap_remove(0); assert_eq!(v.len(), 2); _e = v.swap_remove(1); diff --git a/src/libcore/borrow.rs b/src/libcore/borrow.rs index 4bf356fb552e1..b44b87bd93807 100644 --- a/src/libcore/borrow.rs +++ b/src/libcore/borrow.rs @@ -92,7 +92,7 @@ impl<'a, T, Sized? B> BorrowFrom> for B where B: ToOwned { /// Trait for moving into a `Cow` pub trait IntoCow<'a, T, Sized? B> { - /// Moves `serlf` into `Cow` + /// Moves `self` into `Cow` fn into_cow(self) -> Cow<'a, T, B>; } diff --git a/src/libcoretest/finally.rs b/src/libcoretest/finally.rs index 2d77c1bc0978c..979ddaecb4ae3 100644 --- a/src/libcoretest/finally.rs +++ b/src/libcoretest/finally.rs @@ -9,7 +9,7 @@ // except according to those terms. use core::finally::{try_finally, Finally}; -use std::task::failing; +use std::thread::Thread; #[test] fn test_success() { @@ -20,7 +20,7 @@ fn test_success() { *i = 10; }, |i| { - assert!(!failing()); + assert!(!Thread::panicking()); assert_eq!(*i, 10); *i = 20; }); @@ -38,7 +38,7 @@ fn test_fail() { panic!(); }, |i| { - assert!(failing()); + assert!(Thread::panicking()); assert_eq!(*i, 10); }) } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index ba5157932b61b..120654678e96e 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -475,10 +475,10 @@ pub fn monitor(f: F) { static STACK_SIZE: uint = 32000000; // 32MB let (tx, rx) = channel(); - let mut w = Some(io::ChanWriter::new(tx)); // option dance + let w = io::ChanWriter::new(tx); let mut r = io::ChanReader::new(rx); - let mut cfg = thread::cfg().name("rustc".to_string()); + let mut cfg = thread::Builder::new().name("rustc".to_string()); // FIXME: Hacks on hacks. If the env is trying to override the stack size // then *don't* set it explicitly. @@ -486,11 +486,7 @@ pub fn monitor(f: F) { cfg = cfg.stack_size(STACK_SIZE); } - let f = proc() { - std::io::stdio::set_stderr(box w.take().unwrap()); - f() - }; - match cfg.with_join(f).join() { + match cfg.spawn(move || { std::io::stdio::set_stderr(box w); f() }).join() { Ok(()) => { /* fallthrough */ } Err(value) => { // Task panicked without emitting a fatal diagnostic diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 8ec010a85538a..df7df2f08d983 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -900,7 +900,7 @@ fn run_work_multithreaded(sess: &Session, let mut tx = Some(tx); futures.push(rx); - thread::cfg().name(format!("codegen-{}", i)).spawn(move |:| { + thread::Builder::new().name(format!("codegen-{}", i)).spawn(move |:| { let diag_handler = mk_handler(box diag_emitter); // Must construct cgcx inside the proc because it has non-Send @@ -927,7 +927,7 @@ fn run_work_multithreaded(sess: &Session, } tx.take().unwrap().send(()); - }); + }).detach(); } let mut panicked = false; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9e06b4e88f586..9b99956937c24 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -102,7 +102,11 @@ struct Output { } pub fn main() { - std::os::set_exit_status(main_args(std::os::args().as_slice())); + static STACK_SIZE: uint = 32000000; // 32MB + let res = std::thread::Builder::new().stack_size(STACK_SIZE).spawn(move || { + main_args(std::os::args().as_slice()) + }).join(); + std::os::set_exit_status(res.map_err(|_| ()).unwrap()); } pub fn opts() -> Vec { @@ -343,7 +347,7 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche let cr = Path::new(cratefile); info!("starting to run rustc"); - let (mut krate, analysis) = std::thread::Thread::with_join(move |:| { + let (mut krate, analysis) = std::thread::Thread::spawn(move |:| { let cr = cr; core::run_core(libs, cfgs, externs, &cr, triple) }).join().map_err(|_| "rustc failed").unwrap(); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index d004f030f36af..b55097c0c5a1b 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -155,7 +155,7 @@ fn runtest(test: &str, cratename: &str, libs: Vec, externs: core::Externs, None => box io::stderr() as Box, }; io::util::copy(&mut p, &mut err).unwrap(); - }); + }).detach(); let emitter = diagnostic::EmitterWriter::new(box w2, None); // Compile the code diff --git a/src/librustrt/thread.rs b/src/librustrt/thread.rs deleted file mode 100644 index 175e057c22f2e..0000000000000 --- a/src/librustrt/thread.rs +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright 2013-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. - -//! Native os-thread management -//! -//! This modules contains bindings necessary for managing OS-level threads. -//! These functions operate outside of the rust runtime, creating threads -//! which are not used for scheduling in any way. - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use alloc::boxed::Box; -use core::mem; -use core::uint; -use libc; -use thunk::{Thunk}; - -use stack; -use stack_overflow; - -pub unsafe fn init() { - imp::guard::init(); - stack_overflow::init(); -} - -pub unsafe fn cleanup() { - stack_overflow::cleanup(); -} - -#[cfg(target_os = "windows")] -type StartFn = extern "system" fn(*mut libc::c_void) -> imp::rust_thread_return; - -#[cfg(not(target_os = "windows"))] -type StartFn = extern "C" fn(*mut libc::c_void) -> imp::rust_thread_return; - -/// This struct represents a native thread's state. This is used to join on an -/// existing thread created in the join-able state. -pub struct Thread { - native: imp::rust_thread, - joined: bool, - packet: Box>, -} - -static DEFAULT_STACK_SIZE: uint = 1024 * 1024; - -// This is the starting point of rust os threads. The first thing we do -// is make sure that we don't trigger __morestack (also why this has a -// no_stack_check annotation), and then we extract the main function -// and invoke it. -#[no_stack_check] -fn start_thread(main: *mut libc::c_void) -> imp::rust_thread_return { - unsafe { - stack::record_os_managed_stack_bounds(0, uint::MAX); - let handler = stack_overflow::Handler::new(); - let f: Box = mem::transmute(main); - f.invoke(()); - drop(handler); - mem::transmute(0 as imp::rust_thread_return) - } -} - -#[no_stack_check] -#[cfg(target_os = "windows")] -extern "system" fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return { - return start_thread(main); -} - -#[no_stack_check] -#[cfg(not(target_os = "windows"))] -extern fn thread_start(main: *mut libc::c_void) -> imp::rust_thread_return { - return start_thread(main); -} - -/// Returns the last writable byte of the main thread's stack next to the guard -/// page. Must be called from the main thread. -pub fn main_guard_page() -> uint { - unsafe { - imp::guard::main() - } -} - -/// Returns the last writable byte of the current thread's stack next to the -/// guard page. Must not be called from the main thread. -pub fn current_guard_page() -> uint { - unsafe { - imp::guard::current() - } -} - -// There are two impl blocks b/c if T were specified at the top then it's just a -// pain to specify a type parameter on Thread::spawn (which doesn't need the -// type parameter). -impl Thread<()> { - - /// Starts execution of a new OS thread. - /// - /// This function will not wait for the thread to join, but a handle to the - /// thread will be returned. - /// - /// Note that the handle returned is used to acquire the return value of the - /// procedure `main`. The `join` function will wait for the thread to finish - /// and return the value that `main` generated. - /// - /// Also note that the `Thread` returned will *always* wait for the thread - /// to finish executing. This means that even if `join` is not explicitly - /// called, when the `Thread` falls out of scope its destructor will block - /// waiting for the OS thread. - pub fn start(main: F) -> Thread - where T:Send, F:FnOnce() -> T, F:Send - { - Thread::start_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `start`, but specifies an explicit - /// stack size for the new thread. - pub fn start_stack(stack: uint, main: F) -> Thread - where T:Send, F:FnOnce() -> T, F:Send - { - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let packet = box None; - let packet2: *mut Option = unsafe { - *mem::transmute::<&Box>, *const *mut Option>(&packet) - }; - let native = unsafe { - imp::create(stack, Thunk::new(move |:| { - *packet2 = Some(main.call_once(())); - })) - }; - - Thread { - native: native, - joined: false, - packet: packet, - } - } - - /// This will spawn a new thread, but it will not wait for the thread to - /// finish, nor is it possible to wait for the thread to finish. - /// - /// This corresponds to creating threads in the 'detached' state on unix - /// systems. Note that platforms may not keep the main program alive even if - /// there are detached thread still running around. - pub fn spawn(main: F) - where F : FnOnce() + Send - { - Thread::spawn_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `spawn`, but explicitly specifies a - /// stack size for the new thread. - pub fn spawn_stack(stack: uint, main: F) - where F : FnOnce() + Send - { - unsafe { - let handle = imp::create(stack, Thunk::new(main)); - imp::detach(handle); - } - } - - /// Relinquishes the CPU slot that this OS-thread is currently using, - /// allowing another thread to run for awhile. - pub fn yield_now() { - unsafe { imp::yield_now(); } - } -} - -impl Thread { - /// Wait for this thread to finish, returning the result of the thread's - /// calculation. - pub fn join(mut self) -> T { - assert!(!self.joined); - unsafe { imp::join(self.native) }; - self.joined = true; - assert!(self.packet.is_some()); - self.packet.take().unwrap() - } -} - -#[unsafe_destructor] -impl Drop for Thread { - fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. - if !self.joined { - unsafe { imp::join(self.native) }; - } - } -} - -#[cfg(windows)] -#[allow(non_snake_case)] -mod imp { - use alloc::boxed::Box; - use core::cmp; - use core::mem; - use core::ptr; - use libc; - use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, - LPVOID, DWORD, LPDWORD, HANDLE}; - use stack::RED_ZONE; - use thunk::Thunk; - - pub type rust_thread = HANDLE; - pub type rust_thread_return = DWORD; - - pub mod guard { - pub unsafe fn main() -> uint { - 0 - } - - pub unsafe fn current() -> uint { - 0 - } - - pub unsafe fn init() { - } - } - - pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { - let arg: *mut libc::c_void = mem::transmute(box p); - - // FIXME On UNIX, we guard against stack sizes that are too small but - // that's because pthreads enforces that stacks are at least - // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's - // just that below a certain threshold you can't do anything useful. - // That threshold is application and architecture-specific, however. - // For now, the only requirement is that it's big enough to hold the - // red zone. Round up to the next 64 kB because that's what the NT - // kernel does, might as well make it explicit. With the current - // 20 kB red zone, that makes for a 64 kB minimum stack. - let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); - let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, - super::thread_start, arg, 0, ptr::null_mut()); - - if ret as uint == 0 { - // be sure to not leak the closure - let _p: Box = mem::transmute(arg); - panic!("failed to spawn native thread: {}", ret); - } - return ret; - } - - pub unsafe fn join(native: rust_thread) { - use libc::consts::os::extra::INFINITE; - WaitForSingleObject(native, INFINITE); - } - - pub unsafe fn detach(native: rust_thread) { - assert!(libc::CloseHandle(native) != 0); - } - - pub unsafe fn yield_now() { - // This function will return 0 if there are no other threads to execute, - // but this also means that the yield was useless so this isn't really a - // case that needs to be worried about. - SwitchToThread(); - } - - #[allow(non_snake_case)] - extern "system" { - fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, - dwStackSize: SIZE_T, - lpStartAddress: super::StartFn, - lpParameter: LPVOID, - dwCreationFlags: DWORD, - lpThreadId: LPDWORD) -> HANDLE; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; - fn SwitchToThread() -> BOOL; - } -} - -#[cfg(unix)] -mod imp { - use core::prelude::*; - - use alloc::boxed::Box; - use core::cmp; - use core::mem; - use core::ptr; - use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; - use libc; - use thunk::Thunk; - - use stack::RED_ZONE; - - pub type rust_thread = libc::pthread_t; - pub type rust_thread_return = *mut u8; - - #[cfg(all(not(target_os = "linux"), not(target_os = "macos")))] - pub mod guard { - pub unsafe fn current() -> uint { - 0 - } - - pub unsafe fn main() -> uint { - 0 - } - - pub unsafe fn init() { - } - } - - #[cfg(any(target_os = "linux", target_os = "macos"))] - pub mod guard { - use super::*; - #[cfg(any(target_os = "linux", target_os = "android"))] - use core::mem; - #[cfg(any(target_os = "linux", target_os = "android"))] - use core::ptr; - use libc; - use libc::funcs::posix88::mman::{mmap}; - use libc::consts::os::posix88::{PROT_NONE, - MAP_PRIVATE, - MAP_ANON, - MAP_FAILED, - MAP_FIXED}; - - // These are initialized in init() and only read from after - static mut PAGE_SIZE: uint = 0; - static mut GUARD_PAGE: uint = 0; - - #[cfg(target_os = "macos")] - unsafe fn get_stack_start() -> *mut libc::c_void { - current() as *mut libc::c_void - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - unsafe fn get_stack_start() -> *mut libc::c_void { - let mut attr: libc::pthread_attr_t = mem::zeroed(); - if pthread_getattr_np(pthread_self(), &mut attr) != 0 { - panic!("failed to get thread attributes"); - } - let mut stackaddr = ptr::null_mut(); - let mut stacksize = 0; - if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { - panic!("failed to get stack information"); - } - if pthread_attr_destroy(&mut attr) != 0 { - panic!("failed to destroy thread attributes"); - } - stackaddr - } - - pub unsafe fn init() { - let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); - if psize == -1 { - panic!("failed to get page size"); - } - - PAGE_SIZE = psize as uint; - - let stackaddr = get_stack_start(); - - // Rellocate the last page of the stack. - // This ensures SIGBUS will be raised on - // stack overflow. - let result = mmap(stackaddr, - PAGE_SIZE as libc::size_t, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED, - -1, - 0); - - if result != stackaddr || result == MAP_FAILED { - panic!("failed to allocate a guard page"); - } - - let offset = if cfg!(target_os = "linux") { - 2 - } else { - 1 - }; - - GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; - } - - pub unsafe fn main() -> uint { - GUARD_PAGE - } - - #[cfg(target_os = "macos")] - pub unsafe fn current() -> uint { - (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - - pthread_get_stacksize_np(pthread_self())) as uint - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - pub unsafe fn current() -> uint { - let mut attr: libc::pthread_attr_t = mem::zeroed(); - if pthread_getattr_np(pthread_self(), &mut attr) != 0 { - panic!("failed to get thread attributes"); - } - let mut guardsize = 0; - if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { - panic!("failed to get stack guard page"); - } - if guardsize == 0 { - panic!("there is no guard page"); - } - let mut stackaddr = ptr::null_mut(); - let mut stacksize = 0; - if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { - panic!("failed to get stack information"); - } - if pthread_attr_destroy(&mut attr) != 0 { - panic!("failed to destroy thread attributes"); - } - - stackaddr as uint + guardsize as uint - } - } - - pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { - let mut native: libc::pthread_t = mem::zeroed(); - let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(pthread_attr_init(&mut attr), 0); - assert_eq!(pthread_attr_setdetachstate(&mut attr, - PTHREAD_CREATE_JOINABLE), 0); - - // Reserve room for the red zone, the runtime's stack of last resort. - let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); - match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { - 0 => { - }, - libc::EINVAL => { - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint; - let stack_size = (stack_size + page_size - 1) & - (-(page_size as int - 1) as uint - 1); - assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); - }, - errno => { - // This cannot really happen. - panic!("pthread_attr_setstacksize() error: {}", errno); - }, - }; - - let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint - let ret = pthread_create(&mut native, &attr, super::thread_start, arg); - assert_eq!(pthread_attr_destroy(&mut attr), 0); - - if ret != 0 { - // be sure to not leak the closure - let _p: Box> = mem::transmute(arg); - panic!("failed to spawn native thread: {}", ret); - } - native - } - - pub unsafe fn join(native: rust_thread) { - assert_eq!(pthread_join(native, ptr::null_mut()), 0); - } - - pub unsafe fn detach(native: rust_thread) { - assert_eq!(pthread_detach(native), 0); - } - - pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } - // glibc >= 2.15 has a __pthread_get_minstack() function that returns - // PTHREAD_STACK_MIN plus however many bytes are needed for thread-local - // storage. We need that information to avoid blowing up when a small stack - // is created in an application with big thread-local storage requirements. - // See #6233 for rationale and details. - // - // Link weakly to the symbol for compatibility with older versions of glibc. - // Assumes that we've been dynamically linked to libpthread but that is - // currently always the case. Note that you need to check that the symbol - // is non-null before calling it! - #[cfg(target_os = "linux")] - fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t { - type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; - extern { - #[linkage = "extern_weak"] - static __pthread_get_minstack: *const (); - } - if __pthread_get_minstack.is_null() { - PTHREAD_STACK_MIN - } else { - unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) } - } - } - - // __pthread_get_minstack() is marked as weak but extern_weak linkage is - // not supported on OS X, hence this kludge... - #[cfg(not(target_os = "linux"))] - fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { - PTHREAD_STACK_MIN - } - - #[cfg(any(target_os = "linux"))] - extern { - pub fn pthread_self() -> libc::pthread_t; - pub fn pthread_getattr_np(native: libc::pthread_t, - attr: *mut libc::pthread_attr_t) -> libc::c_int; - pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, - guardsize: *mut libc::size_t) -> libc::c_int; - pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, - stackaddr: *mut *mut libc::c_void, - stacksize: *mut libc::size_t) -> libc::c_int; - } - - #[cfg(target_os = "macos")] - extern { - pub fn pthread_self() -> libc::pthread_t; - pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; - pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; - } - - extern { - fn pthread_create(native: *mut libc::pthread_t, - attr: *const libc::pthread_attr_t, - f: super::StartFn, - value: *mut libc::c_void) -> libc::c_int; - fn pthread_join(native: libc::pthread_t, - value: *mut *mut libc::c_void) -> libc::c_int; - fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; - pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; - fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t) -> libc::c_int; - fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, - state: libc::c_int) -> libc::c_int; - fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; - fn sched_yield() -> libc::c_int; - } -} - -#[cfg(test)] -mod tests { - use super::Thread; - - #[test] - fn smoke() { Thread::start(move|| {}).join(); } - - #[test] - fn data() { assert_eq!(Thread::start(move|| { 1i }).join(), 1); } - - #[test] - fn detached() { Thread::spawn(move|| {}) } - - #[test] - fn small_stacks() { - assert_eq!(42i, Thread::start_stack(0, move|| 42i).join()); - assert_eq!(42i, Thread::start_stack(1, move|| 42i).join()); - } -} diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 6bfb219d9c44e..8fe3642e702d4 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -637,7 +637,7 @@ mod tests { #[test] fn test_to_c_str_fail() { - assert!(Thread::with_join(move|| { "he\x00llo".to_c_str() }).join().is_err()); + assert!(Thread::spawn(move|| { "he\x00llo".to_c_str() }).join().is_err()); } #[test] diff --git a/src/libstd/comm/blocking.rs b/src/libstd/comm/blocking.rs index bb09726575638..c477acd70aa8d 100644 --- a/src/libstd/comm/blocking.rs +++ b/src/libstd/comm/blocking.rs @@ -13,7 +13,7 @@ use thread::Thread; use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Ordering}; use sync::Arc; -use kinds::marker::NoSend; +use kinds::marker::{NoSend, NoSync}; use mem; use clone::Clone; @@ -30,6 +30,7 @@ pub struct SignalToken { pub struct WaitToken { inner: Arc, no_send: NoSend, + no_sync: NoSync, } pub fn tokens() -> (WaitToken, SignalToken) { @@ -40,6 +41,7 @@ pub fn tokens() -> (WaitToken, SignalToken) { let wait_token = WaitToken { inner: inner.clone(), no_send: NoSend, + no_sync: NoSync, }; let signal_token = SignalToken { inner: inner diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 236a055b91ea7..4977f966eba29 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -59,26 +59,30 @@ //! Simple usage: //! //! ``` +//! use std::thread::Thread; +//! //! // Create a simple streaming channel //! let (tx, rx) = channel(); -//! spawn(move|| { +//! Thread::spawn(move|| { //! tx.send(10i); -//! }); +//! }).detach(); //! assert_eq!(rx.recv(), 10i); //! ``` //! //! Shared usage: //! //! ``` -//! // Create a shared channel that can be sent along from many tasks +//! use std::thread::Thread; +//! +//! // Create a shared channel that can be sent along from many threads //! // where tx is the sending half (tx for transmission), and rx is the receiving //! // half (rx for receiving). //! let (tx, rx) = channel(); //! for i in range(0i, 10i) { //! let tx = tx.clone(); -//! spawn(move|| { +//! Thread::spawn(move|| { //! tx.send(i); -//! }) +//! }).detach() //! } //! //! for _ in range(0i, 10i) { @@ -100,11 +104,13 @@ //! Synchronous channels: //! //! ``` +//! use std::thread::Thread; +//! //! let (tx, rx) = sync_channel::(0); -//! spawn(move|| { +//! Thread::spawn(move|| { //! // This will wait for the parent task to start receiving //! tx.send(53); -//! }); +//! }).detach(); //! rx.recv(); //! ``` //! @@ -451,15 +457,17 @@ impl UnsafeFlavor for Receiver { /// # Example /// /// ``` +/// use std::thread::Thread; +/// /// // tx is is the sending half (tx for transmission), and rx is the receiving /// // half (rx for receiving). /// let (tx, rx) = channel(); /// /// // Spawn off an expensive computation -/// spawn(move|| { +/// Thread::spawn(move|| { /// # fn expensive_computation() {} /// tx.send(expensive_computation()); -/// }); +/// }).detach(); /// /// // Do some useful work for awhile /// @@ -490,15 +498,17 @@ pub fn channel() -> (Sender, Receiver) { /// # Example /// /// ``` +/// use std::thread::Thread; +/// /// let (tx, rx) = sync_channel(1); /// /// // this returns immediately /// tx.send(1i); /// -/// spawn(move|| { +/// Thread::spawn(move|| { /// // this will block until the previous message has been received /// tx.send(2i); -/// }); +/// }).detach(); /// /// assert_eq!(rx.recv(), 1i); /// assert_eq!(rx.recv(), 2i); @@ -1242,7 +1252,7 @@ mod test { test! { fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will panic - let res = Thread::with_join(move|| { + let res = Thread::spawn(move|| { let (tx, rx) = channel::(); drop(tx); rx.recv(); @@ -1314,7 +1324,7 @@ mod test { spawn(move|| { drop(tx); }); - let res = Thread::with_join(move|| { + let res = Thread::spawn(move|| { assert!(rx.recv() == box 10); }).join(); assert!(res.is_err()); @@ -1336,7 +1346,7 @@ mod test { spawn(move|| { drop(rx); }); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { tx.send(1); }).join(); } @@ -1345,8 +1355,8 @@ mod test { test! { fn oneshot_multi_thread_recv_close_stress() { for _ in range(0, stress_factor()) { let (tx, rx) = channel::(); - spawn(proc() { - let res = Thread::with_join(move|| { + spawn(move|| { + let res = Thread::spawn(move|| { rx.recv(); }).join(); assert!(res.is_err()); @@ -1664,7 +1674,7 @@ mod sync_tests { test! { fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will panic - let res = Thread::with_join(move|| { + let res = Thread::spawn(move|| { let (tx, rx) = sync_channel::(0); drop(tx); rx.recv(); @@ -1741,7 +1751,7 @@ mod sync_tests { spawn(move|| { drop(tx); }); - let res = Thread::with_join(move|| { + let res = Thread::spawn(move|| { assert!(rx.recv() == box 10); }).join(); assert!(res.is_err()); @@ -1763,7 +1773,7 @@ mod sync_tests { spawn(move|| { drop(rx); }); - let _ = Thread::with_join(move || { + let _ = Thread::spawn(move || { tx.send(1); }).join(); } @@ -1772,8 +1782,8 @@ mod sync_tests { test! { fn oneshot_multi_thread_recv_close_stress() { for _ in range(0, stress_factor()) { let (tx, rx) = sync_channel::(0); - spawn(proc() { - let res = Thread::with_join(move|| { + spawn(move|| { + let res = Thread::spawn(move|| { rx.recv(); }).join(); assert!(res.is_err()); diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs index b24c6d21fba6d..f75186e70e3d4 100644 --- a/src/libstd/comm/sync.rs +++ b/src/libstd/comm/sync.rs @@ -108,20 +108,18 @@ fn wait<'a, 'b, T: Send>(lock: &'a Mutex>, f: fn(SignalToken) -> Blocker) -> MutexGuard<'a, State> { - let me: Box = Local::take(); - me.deschedule(1, |task| { - match mem::replace(&mut guard.blocker, f(task)) { - NoneBlocked => {} - _ => unreachable!(), - } - mem::drop(guard); - Ok(()) - }); - lock.lock() + let (wait_token, signal_token) = blocking::tokens(); + match mem::replace(&mut guard.blocker, f(signal_token)) { + NoneBlocked => {} + _ => unreachable!(), + } + drop(guard); // unlock + wait_token.wait(); // block + lock.lock() // relock } -/// Wakes up a task, dropping the lock at the correct time -fn wakeup(task: BlockedTask, guard: MutexGuard>) { +/// Wakes up a thread, dropping the lock at the correct time +fn wakeup(token: SignalToken, guard: MutexGuard>) { // We need to be careful to wake up the waiting task *outside* of the mutex // in case it incurs a context switch. drop(guard); diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index d2a9861737d12..e865bf42bd01d 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -167,7 +167,7 @@ mod test { tx.send(vec![3u8, 4u8]); tx.send(vec![5u8, 6u8]); tx.send(vec![7u8, 8u8]); - }); + }).detach(); let mut reader = ChanReader::new(rx); let mut buf = [0u8, ..3]; @@ -210,7 +210,7 @@ mod test { tx.send(b"rld\nhow ".to_vec()); tx.send(b"are you?".to_vec()); tx.send(b"".to_vec()); - }); + }).detach(); let mut reader = ChanReader::new(rx); @@ -229,11 +229,7 @@ mod test { writer.write_be_u32(42).unwrap(); let wanted = vec![0u8, 0u8, 0u8, 42u8]; -<<<<<<< HEAD - let got = match task::try(move|| { rx.recv() }) { -======= - let got = match Thread::with_join(proc() { rx.recv() }).join() { ->>>>>>> Fallout from new thread API + let got = match Thread::spawn(move|| { rx.recv() }).join() { Ok(got) => got, Err(_) => panic!(), }; diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 6a6d467e86c04..5807a3bc4662e 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -105,6 +105,7 @@ //! # #![allow(dead_code)] //! use std::io::{TcpListener, TcpStream}; //! use std::io::{Acceptor, Listener}; +//! use std::thread::Thread; //! //! let listener = TcpListener::bind("127.0.0.1:80"); //! @@ -119,10 +120,10 @@ //! for stream in acceptor.incoming() { //! match stream { //! Err(e) => { /* connection failed */ } -//! Ok(stream) => spawn(move|| { +//! Ok(stream) => Thread::spawn(move|| { //! // connection succeeded //! handle_client(stream) -//! }) +//! }).detach() //! } //! } //! diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 0e9a93e42758c..6bc952f30af6c 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -136,16 +136,17 @@ impl TcpStream { /// use std::io::timer; /// use std::io::TcpStream; /// use std::time::Duration; + /// use std::thread::Thread; /// /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); /// let stream2 = stream.clone(); /// - /// spawn(move|| { + /// Thread::spawn(move|| { /// // close this stream after one second /// timer::sleep(Duration::seconds(1)); /// let mut stream = stream2; /// stream.close_read(); - /// }); + /// }).detach(); /// /// // wait for some data, will get canceled after one second /// let mut buf = [0]; @@ -279,6 +280,7 @@ impl sys_common::AsInner for TcpStream { /// # #![allow(dead_code)] /// use std::io::{TcpListener, TcpStream}; /// use std::io::{Acceptor, Listener}; +/// use std::thread::Thread; /// /// let listener = TcpListener::bind("127.0.0.1:80"); /// @@ -293,10 +295,10 @@ impl sys_common::AsInner for TcpStream { /// for stream in acceptor.incoming() { /// match stream { /// Err(e) => { /* connection failed */ } -/// Ok(stream) => spawn(move|| { +/// Ok(stream) => Thread::spawn(move|| { /// // connection succeeded /// handle_client(stream) -/// }) +/// }).detach() /// } /// } /// @@ -416,11 +418,12 @@ impl TcpAcceptor { /// ``` /// # #![allow(experimental)] /// use std::io::{TcpListener, Listener, Acceptor, EndOfFile}; + /// use std::thread::Thread; /// /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); /// let a2 = a.clone(); /// - /// spawn(move|| { + /// Thread::spawn(move|| { /// let mut a2 = a2; /// for socket in a2.incoming() { /// match socket { @@ -429,7 +432,7 @@ impl TcpAcceptor { /// Err(e) => panic!("unexpected error: {}", e), /// } /// } - /// }); + /// }).detach(); /// /// # fn wait_for_sigint() {} /// // Now that our accept loop is running, wait for the program to be diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index d9acb94714b24..60360a2bc6445 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -698,7 +698,7 @@ impl Process { Thread::spawn(move |:| { let mut stream = stream; tx.send(stream.read_to_end()) - }); + }).detach(); } None => tx.send(Ok(Vec::new())) } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 5796f8946611a..78c194745a888 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -230,7 +230,6 @@ pub mod hash; /* Threads and communication */ pub mod task; -#[allow(missing_docs)] pub mod thread; pub mod sync; pub mod comm; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 798dac1a72f9c..3d03b5324b9c1 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -353,13 +353,15 @@ macro_rules! vec { /// # Example /// /// ``` +/// use std::thread::Thread; +/// /// let (tx1, rx1) = channel(); /// let (tx2, rx2) = channel(); /// # fn long_running_task() {} /// # fn calculate_the_answer() -> int { 42i } /// -/// spawn(move|| { long_running_task(); tx1.send(()) }); -/// spawn(move|| { tx2.send(calculate_the_answer()) }); +/// Thread::spawn(move|| { long_running_task(); tx1.send(()) }).detach(); +/// Thread::spawn(move|| { tx2.send(calculate_the_answer()) }).detach(); /// /// select! ( /// () = rx1.recv() => println!("the long running task finished first"), diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 9ec3128302c11..a049ea01b6d49 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -26,6 +26,7 @@ #![allow(missing_docs)] #![allow(non_snake_case)] +#![allow(unused_imports)] pub use self::MemoryMapKind::*; pub use self::MapOption::*; @@ -37,7 +38,7 @@ use fmt; use io::{IoResult, IoError}; use iter::{Iterator, IteratorExt}; use kinds::Copy; -use libc::{c_void, c_int}; +use libc::{c_void, c_int, c_char}; use libc; use boxed::Box; use ops::{Drop, FnOnce}; diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 5ab8eb6b9422d..62f64159c047a 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -515,17 +515,17 @@ mod tests { #[test] fn test_null_byte() { use thread::Thread; - let result = Thread::with_join(move|| { + let result = Thread::spawn(move|| { Path::new(b"foo/bar\0") }).join(); assert!(result.is_err()); - let result = Thread::with_join(move|| { + let result = Thread::spawn(move|| { Path::new("test").set_filename(b"f\0o") }).join(); assert!(result.is_err()); - let result = Thread::with_join(move|| { + let result = Thread::spawn(move|| { Path::new("test").push(b"f\0o"); }).join(); assert!(result.is_err()); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 171707c10c5da..5cbefb0d3d8e0 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -1299,17 +1299,17 @@ mod tests { #[test] fn test_null_byte() { use thread::Thread; - let result = Thread::with_join(move|| { + let result = Thread::spawn(move|| { Path::new(b"foo/bar\0") }).join(); assert!(result.is_err()); - let result = Thread::with_join(move|| { + let result = Thread::spawn(move|| { Path::new("test").set_filename(b"f\0o") }).join(); assert!(result.is_err()); - let result = Thread::with_join(move|| { + let result = Thread::spawn(move || { Path::new("test").push(b"f\0o"); }).join(); assert!(result.is_err()); diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index d2ae66d657809..dbe5f9193298a 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -379,7 +379,7 @@ mod test { r.fill_bytes(&mut v); Thread::yield_now(); } - }); + }).detach(); } // start all the tasks diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index 1b97a01146c43..b63b4ced00545 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -45,7 +45,7 @@ pub fn cleanup() { let queue: Box = mem::transmute(queue); let v = mem::replace(&mut *queue.lock(), Vec::new()); for to_run in v.into_iter() { - to_run.invoke(); + to_run.invoke(()); } } } diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs deleted file mode 100644 index 089960a6bc80b..0000000000000 --- a/src/libstd/rt/local.rs +++ /dev/null @@ -1,131 +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 core::prelude::*; - -use boxed::Box; -use rt::local_ptr; -use rt::task::Task; - -/// Encapsulates some task-local data. -pub trait Local { - fn put(value: Box); - fn take() -> Box; - fn try_take() -> Option>; - fn exists(unused_value: Option) -> bool; - fn borrow(unused_value: Option) -> Borrowed; - unsafe fn unsafe_take() -> Box; - unsafe fn unsafe_borrow() -> *mut Self; - unsafe fn try_unsafe_borrow() -> Option<*mut Self>; -} - -impl Local> for Task { - #[inline] - fn put(value: Box) { unsafe { local_ptr::put(value) } } - #[inline] - fn take() -> Box { unsafe { local_ptr::take() } } - #[inline] - fn try_take() -> Option> { unsafe { local_ptr::try_take() } } - fn exists(_: Option) -> bool { local_ptr::exists() } - #[inline] - fn borrow(_: Option) -> local_ptr::Borrowed { - unsafe { - local_ptr::borrow::() - } - } - #[inline] - unsafe fn unsafe_take() -> Box { local_ptr::unsafe_take() } - #[inline] - unsafe fn unsafe_borrow() -> *mut Task { local_ptr::unsafe_borrow() } - #[inline] - unsafe fn try_unsafe_borrow() -> Option<*mut Task> { - local_ptr::try_unsafe_borrow() - } -} - -#[cfg(test)] -mod test { - use prelude::*; - use super::*; - use super::super::thread::Thread; - use super::super::task::Task; - - #[test] - fn thread_local_task_smoke_test() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - let task: Box = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn thread_local_task_two_instances() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - let task: Box = Local::take(); - cleanup_task(task); - let task = box Task::new(None, None); - Local::put(task); - let task: Box = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn borrow_smoke_test() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - unsafe { - let _task: *mut Task = Local::unsafe_borrow(); - } - let task: Box = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn borrow_with_return() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - { - let _ = Local::borrow(None::); - } - - let task: Box = Local::take(); - cleanup_task(task); - }).join(); - } - - #[test] - fn try_take() { - Thread::start(move|| { - let task = box Task::new(None, None); - Local::put(task); - - let t: Box = Local::try_take().unwrap(); - let u: Option> = Local::try_take(); - assert!(u.is_none()); - - cleanup_task(t); - }).join(); - } - - fn cleanup_task(t: Box) { - t.drop(); - } - -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index acc05cbf14032..fd50d845716b9 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -27,6 +27,7 @@ use os; use thunk::Thunk; use kinds::Send; use thread::Thread; +use ops::FnOnce; use sys; use sys_common; use sys_common::thread_info::{mod, NewThread}; @@ -145,8 +146,8 @@ fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { /// /// 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); +pub fn at_exit(f: F) { + at_exit_imp::push(Thunk::new(f)); } /// One-time runtime cleanup. diff --git a/src/libstd/rt/mutex.rs b/src/libstd/rt/mutex.rs deleted file mode 100644 index 381f14570dfbc..0000000000000 --- a/src/libstd/rt/mutex.rs +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2013-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. - -//! A native mutex and condition variable type. -//! -//! This module contains bindings to the platform's native mutex/condition -//! variable primitives. It provides two types: `StaticNativeMutex`, which can -//! be statically initialized via the `NATIVE_MUTEX_INIT` value, and a simple -//! wrapper `NativeMutex` that has a destructor to clean up after itself. These -//! objects serve as both mutexes and condition variables simultaneously. -//! -//! The static lock is lazily initialized, but it can only be unsafely -//! destroyed. A statically initialized lock doesn't necessarily have a time at -//! which it can get deallocated. For this reason, there is no `Drop` -//! implementation of the static mutex, but rather the `destroy()` method must -//! be invoked manually if destruction of the mutex is desired. -//! -//! The non-static `NativeMutex` type does have a destructor, but cannot be -//! statically initialized. -//! -//! It is not recommended to use this type for idiomatic rust use. These types -//! are appropriate where no other options are available, but other rust -//! concurrency primitives should be used before them: the `sync` crate defines -//! `StaticMutex` and `Mutex` types. -//! -//! # Example -//! -//! ```rust -//! use rt::mutex::{NativeMutex, StaticNativeMutex, NATIVE_MUTEX_INIT}; -//! -//! // Use a statically initialized mutex -//! static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; -//! -//! unsafe { -//! let _guard = LOCK.lock(); -//! } // automatically unlocked here -//! -//! // Use a normally initialized mutex -//! unsafe { -//! let mut lock = NativeMutex::new(); -//! -//! { -//! let _guard = lock.lock(); -//! } // unlocked here -//! -//! // sometimes the RAII guard isn't appropriate -//! lock.lock_noguard(); -//! lock.unlock_noguard(); -//! } // `lock` is deallocated here -//! ``` - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use sys::mutex as imp; - -/// A native mutex suitable for storing in statics (that is, it has -/// the `destroy` method rather than a destructor). -/// -/// Prefer the `NativeMutex` type where possible, since that does not -/// require manual deallocation. -pub struct StaticNativeMutex { - inner: imp::Mutex, -} - -/// A native mutex with a destructor for clean-up. -/// -/// See `StaticNativeMutex` for a version that is suitable for storing in -/// statics. -pub struct NativeMutex { - inner: StaticNativeMutex -} - -/// Automatically unlocks the mutex that it was created from on -/// destruction. -/// -/// Using this makes lock-based code resilient to unwinding/task -/// panic, because the lock will be automatically unlocked even -/// then. -#[must_use] -pub struct LockGuard<'a> { - lock: &'a StaticNativeMutex -} - -pub const NATIVE_MUTEX_INIT: StaticNativeMutex = StaticNativeMutex { - inner: imp::MUTEX_INIT, -}; - -impl StaticNativeMutex { - /// Creates a new mutex. - /// - /// Note that a mutex created in this way needs to be explicit - /// freed with a call to `destroy` or it will leak. - /// Also it is important to avoid locking until mutex has stopped moving - pub unsafe fn new() -> StaticNativeMutex { - StaticNativeMutex { inner: imp::Mutex::new() } - } - - /// Acquires this lock. This assumes that the current thread does not - /// already hold the lock. - /// - /// # Example - /// - /// ```rust - /// use rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - /// static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - /// unsafe { - /// let _guard = LOCK.lock(); - /// // critical section... - /// } // automatically unlocked in `_guard`'s destructor - /// ``` - /// - /// # Unsafety - /// - /// This method is unsafe because it will not function correctly if this - /// mutex has been *moved* since it was last used. The mutex can move an - /// arbitrary number of times before its first usage, but once a mutex has - /// been used once it is no longer allowed to move (or otherwise it invokes - /// undefined behavior). - /// - /// Additionally, this type does not take into account any form of - /// scheduling model. This will unconditionally block the *os thread* which - /// is not always desired. - pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> { - self.inner.lock(); - - LockGuard { lock: self } - } - - /// Attempts to acquire the lock. The value returned is `Some` if - /// the attempt succeeded. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. - pub unsafe fn trylock<'a>(&'a self) -> Option> { - if self.inner.trylock() { - Some(LockGuard { lock: self }) - } else { - None - } - } - - /// Acquire the lock without creating a `LockGuard`. - /// - /// These needs to be paired with a call to `.unlock_noguard`. Prefer using - /// `.lock`. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// does not guarantee that the mutex will ever be unlocked, and it is - /// undefined to drop an already-locked mutex. - pub unsafe fn lock_noguard(&self) { self.inner.lock() } - - /// Attempts to acquire the lock without creating a - /// `LockGuard`. The value returned is whether the lock was - /// acquired or not. - /// - /// If `true` is returned, this needs to be paired with a call to - /// `.unlock_noguard`. Prefer using `.trylock`. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock_noguard`. - pub unsafe fn trylock_noguard(&self) -> bool { - self.inner.trylock() - } - - /// Unlocks the lock. This assumes that the current thread already holds the - /// lock. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, it - /// is not guaranteed that this is unlocking a previously locked mutex. It - /// is undefined to unlock an unlocked mutex. - pub unsafe fn unlock_noguard(&self) { self.inner.unlock() } - - /// Block on the internal condition variable. - /// - /// This function assumes that the lock is already held. Prefer - /// using `LockGuard.wait` since that guarantees that the lock is - /// held. - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// is unsafe because the mutex may not be currently locked. - pub unsafe fn wait_noguard(&self) { self.inner.wait() } - - /// Signals a thread in `wait` to wake up - /// - /// # Unsafety - /// - /// This method is unsafe for the same reasons as `lock`. Additionally, this - /// is unsafe because the mutex may not be currently locked. - pub unsafe fn signal_noguard(&self) { self.inner.signal() } - - /// This function is especially unsafe because there are no guarantees made - /// that no other thread is currently holding the lock or waiting on the - /// condition variable contained inside. - pub unsafe fn destroy(&self) { self.inner.destroy() } -} - -impl NativeMutex { - /// Creates a new mutex. - /// - /// The user must be careful to ensure the mutex is not locked when its is - /// being destroyed. - /// Also it is important to avoid locking until mutex has stopped moving - pub unsafe fn new() -> NativeMutex { - NativeMutex { inner: StaticNativeMutex::new() } - } - - /// Acquires this lock. This assumes that the current thread does not - /// already hold the lock. - /// - /// # Example - /// - /// ```rust - /// use rt::mutex::NativeMutex; - /// unsafe { - /// let mut lock = NativeMutex::new(); - /// - /// { - /// let _guard = lock.lock(); - /// // critical section... - /// } // automatically unlocked in `_guard`'s destructor - /// } - /// ``` - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::lock`. - pub unsafe fn lock<'a>(&'a self) -> LockGuard<'a> { - self.inner.lock() - } - - /// Attempts to acquire the lock. The value returned is `Some` if - /// the attempt succeeded. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::trylock`. - pub unsafe fn trylock<'a>(&'a self) -> Option> { - self.inner.trylock() - } - - /// Acquire the lock without creating a `LockGuard`. - /// - /// These needs to be paired with a call to `.unlock_noguard`. Prefer using - /// `.lock`. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::lock_noguard`. - pub unsafe fn lock_noguard(&self) { self.inner.lock_noguard() } - - /// Attempts to acquire the lock without creating a - /// `LockGuard`. The value returned is whether the lock was - /// acquired or not. - /// - /// If `true` is returned, this needs to be paired with a call to - /// `.unlock_noguard`. Prefer using `.trylock`. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::trylock_noguard`. - pub unsafe fn trylock_noguard(&self) -> bool { - self.inner.trylock_noguard() - } - - /// Unlocks the lock. This assumes that the current thread already holds the - /// lock. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::unlock_noguard`. - pub unsafe fn unlock_noguard(&self) { self.inner.unlock_noguard() } - - /// Block on the internal condition variable. - /// - /// This function assumes that the lock is already held. Prefer - /// using `LockGuard.wait` since that guarantees that the lock is - /// held. - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::wait_noguard`. - pub unsafe fn wait_noguard(&self) { self.inner.wait_noguard() } - - /// Signals a thread in `wait` to wake up - /// - /// # Unsafety - /// - /// This method is unsafe due to the same reasons as - /// `StaticNativeMutex::signal_noguard`. - pub unsafe fn signal_noguard(&self) { self.inner.signal_noguard() } -} - -impl Drop for NativeMutex { - fn drop(&mut self) { - unsafe {self.inner.destroy()} - } -} - -impl<'a> LockGuard<'a> { - /// Block on the internal condition variable. - pub unsafe fn wait(&self) { - self.lock.wait_noguard() - } - - /// Signals a thread in `wait` to wake up. - pub unsafe fn signal(&self) { - self.lock.signal_noguard() - } -} - -#[unsafe_destructor] -impl<'a> Drop for LockGuard<'a> { - fn drop(&mut self) { - unsafe {self.lock.unlock_noguard()} - } -} - -#[cfg(test)] -mod test { - use prelude::*; - - use mem::drop; - use super::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - use rt::thread::Thread; - - #[test] - fn smoke_lock() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - let _guard = LK.lock(); - } - } - - #[test] - fn smoke_cond() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - let guard = LK.lock(); - let t = Thread::start(move|| { - let guard = LK.lock(); - guard.signal(); - }); - guard.wait(); - drop(guard); - - t.join(); - } - } - - #[test] - fn smoke_lock_noguard() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - LK.lock_noguard(); - LK.unlock_noguard(); - } - } - - #[test] - fn smoke_cond_noguard() { - static LK: StaticNativeMutex = NATIVE_MUTEX_INIT; - unsafe { - LK.lock_noguard(); - let t = Thread::start(move|| { - LK.lock_noguard(); - LK.signal_noguard(); - LK.unlock_noguard(); - }); - LK.wait_noguard(); - LK.unlock_noguard(); - - t.join(); - } - } - - #[test] - fn destroy_immediately() { - unsafe { - let m = StaticNativeMutex::new(); - m.destroy(); - } - } -} diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs deleted file mode 100644 index 9eb7048f1e699..0000000000000 --- a/src/libstd/rt/thread.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2013-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. - -//! Native os-thread management -//! -//! This modules contains bindings necessary for managing OS-level threads. -//! These functions operate outside of the rust runtime, creating threads -//! which are not used for scheduling in any way. - -#![allow(non_camel_case_types)] - -use core::prelude::*; - -use boxed::Box; -use mem; -use sys::stack_overflow; -use sys::thread as imp; - -pub unsafe fn init() { - imp::guard::init(); - stack_overflow::init(); -} - -pub unsafe fn cleanup() { - stack_overflow::cleanup(); -} - -/// This struct represents a native thread's state. This is used to join on an -/// existing thread created in the join-able state. -pub struct Thread { - native: imp::rust_thread, - joined: bool, - packet: Box>, -} - -static DEFAULT_STACK_SIZE: uint = 1024 * 1024; - -/// Returns the last writable byte of the main thread's stack next to the guard -/// page. Must be called from the main thread. -pub fn main_guard_page() -> uint { - unsafe { - imp::guard::main() - } -} - -/// Returns the last writable byte of the current thread's stack next to the -/// guard page. Must not be called from the main thread. -pub fn current_guard_page() -> uint { - unsafe { - imp::guard::current() - } -} - -// There are two impl blocks b/c if T were specified at the top then it's just a -// pain to specify a type parameter on Thread::spawn (which doesn't need the -// type parameter). -impl Thread<()> { - /// Starts execution of a new OS thread. - /// - /// This function will not wait for the thread to join, but a handle to the - /// thread will be returned. - /// - /// Note that the handle returned is used to acquire the return value of the - /// procedure `main`. The `join` function will wait for the thread to finish - /// and return the value that `main` generated. - /// - /// Also note that the `Thread` returned will *always* wait for the thread - /// to finish executing. This means that even if `join` is not explicitly - /// called, when the `Thread` falls out of scope its destructor will block - /// waiting for the OS thread. - pub fn start(main: proc():Send -> T) -> Thread { - Thread::start_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `start`, but specifies an explicit - /// stack size for the new thread. - pub fn start_stack(stack: uint, main: proc():Send -> T) -> Thread { - - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let packet = box None; - let packet2: *mut Option = unsafe { - *mem::transmute::<&Box>, *const *mut Option>(&packet) - }; - let main = proc() unsafe { *packet2 = Some(main()); }; - let native = unsafe { imp::create(stack, box main) }; - - Thread { - native: native, - joined: false, - packet: packet, - } - } - - /// This will spawn a new thread, but it will not wait for the thread to - /// finish, nor is it possible to wait for the thread to finish. - /// - /// This corresponds to creating threads in the 'detached' state on unix - /// systems. Note that platforms may not keep the main program alive even if - /// there are detached thread still running around. - pub fn spawn(main: proc():Send) { - Thread::spawn_stack(DEFAULT_STACK_SIZE, main) - } - - /// Performs the same functionality as `spawn`, but explicitly specifies a - /// stack size for the new thread. - pub fn spawn_stack(stack: uint, main: proc():Send) { - unsafe { - let handle = imp::create(stack, box main); - imp::detach(handle); - } - } - - /// Relinquishes the CPU slot that this OS-thread is currently using, - /// allowing another thread to run for awhile. - pub fn yield_now() { - unsafe { imp::yield_now(); } - } -} - -impl Thread { - /// Wait for this thread to finish, returning the result of the thread's - /// calculation. - pub fn join(mut self) -> T { - assert!(!self.joined); - unsafe { imp::join(self.native) }; - self.joined = true; - assert!(self.packet.is_some()); - self.packet.take().unwrap() - } -} - -#[unsafe_destructor] -impl Drop for Thread { - fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. - if !self.joined { - unsafe { imp::join(self.native) }; - } - } -} - -#[cfg(test)] -mod tests { - use super::Thread; - - #[test] - fn smoke() { Thread::start(proc (){}).join(); } - - #[test] - fn data() { assert_eq!(Thread::start(proc () { 1i }).join(), 1); } - - #[test] - fn detached() { Thread::spawn(proc () {}) } - - #[test] - fn small_stacks() { - assert_eq!(42i, Thread::start_stack(0, proc () 42i).join()); - assert_eq!(42i, Thread::start_stack(1, proc () 42i).join()); - } -} diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index c896f4e39daef..8ef10cbbd77db 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -373,6 +373,7 @@ pub mod eabi { pub use self::EXCEPTION_DISPOSITION::*; use rt::libunwind as uw; use libc::{c_void, c_int}; + use kinds::Copy; #[repr(C)] #[allow(missing_copy_implementations)] diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 86dbb6066f30e..fa527a70f8395 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -89,6 +89,7 @@ pub fn default_sched_threads() -> uint { pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || cfg!(rtassert); +#[allow(missing_copy_implementations)] pub struct Stdio(libc::c_int); #[allow(non_upper_case_globals)] diff --git a/src/libstd/sync/atomic.rs b/src/libstd/sync/atomic.rs index fe5b962fa4b74..26778ef70b3ca 100644 --- a/src/libstd/sync/atomic.rs +++ b/src/libstd/sync/atomic.rs @@ -42,14 +42,15 @@ //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUint, SeqCst}; +//! use std::thread::Thread; //! //! fn main() { //! let spinlock = Arc::new(AtomicUint::new(1)); //! //! let spinlock_clone = spinlock.clone(); -//! spawn(move|| { +//! Thread::spawn(move|| { //! spinlock_clone.store(0, SeqCst); -//! }); +//! }).detach(); //! //! // Wait for the other task to release the lock //! while spinlock.load(SeqCst) != 0 {} @@ -61,6 +62,7 @@ //! ``` //! use std::sync::Arc; //! use std::sync::atomic::{AtomicOption, SeqCst}; +//! use std::thread::Thread; //! //! fn main() { //! struct BigObject; @@ -68,14 +70,14 @@ //! let shared_big_object = Arc::new(AtomicOption::empty()); //! //! let shared_big_object_clone = shared_big_object.clone(); -//! spawn(move|| { +//! Thread::spawn(move|| { //! let unwrapped_big_object = shared_big_object_clone.take(SeqCst); //! if unwrapped_big_object.is_some() { //! println!("got a big object from another task"); //! } else { //! println!("other task hasn't sent big object yet"); //! } -//! }); +//! }).detach(); //! //! shared_big_object.swap(box BigObject, SeqCst); //! } diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index 1b8574604a0c1..6573d9273ceff 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -15,17 +15,18 @@ use sync::{Mutex, Condvar}; /// /// ```rust /// use std::sync::{Arc, Barrier}; +/// use std::thread::Thread; /// /// let barrier = Arc::new(Barrier::new(10)); /// for _ in range(0u, 10) { /// let c = barrier.clone(); /// // The same messages will be printed together. /// // You will NOT see any interleaving. -/// spawn(move|| { +/// Thread::spawn(move|| { /// println!("before wait"); /// c.wait(); /// println!("after wait"); -/// }); +/// }).detach(); /// } /// ``` pub struct Barrier { diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 3bdab261e1957..be27c06b83cd3 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -36,17 +36,18 @@ use time::Duration; /// /// ``` /// use std::sync::{Arc, Mutex, Condvar}; +/// use std::thread::Thread; /// /// let pair = Arc::new((Mutex::new(false), Condvar::new())); /// let pair2 = pair.clone(); /// /// // Inside of our lock, spawn a new thread, and then wait for it to start -/// spawn(move|| { +/// Thread::spawn(move|| { /// let &(ref lock, ref cvar) = &*pair2; /// let mut started = lock.lock(); /// *started = true; /// cvar.notify_one(); -/// }); +/// }).detach(); /// /// // wait for the thread to start up /// let &(ref lock, ref cvar) = &*pair; @@ -362,4 +363,3 @@ mod tests { } } - diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index 16f2cff599879..5e9d234c6423d 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -142,7 +142,7 @@ impl Future { Thread::spawn(move |:| { // Don't panic if the other end has hung up let _ = tx.send_opt(blk()); - }); + }).detach(); Future::from_receiver(rx) } diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index fc73e2957a55f..4829be569cc97 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -35,6 +35,7 @@ use sys_common::mutex as sys; /// /// ```rust /// use std::sync::{Arc, Mutex}; +/// use std::thread::Thread; /// const N: uint = 10; /// /// // Spawn a few threads to increment a shared variable (non-atomically), and @@ -47,7 +48,7 @@ use sys_common::mutex as sys; /// let (tx, rx) = channel(); /// for _ in range(0u, 10) { /// let (data, tx) = (data.clone(), tx.clone()); -/// spawn(move|| { +/// Thread::spawn(move|| { /// // The shared static can only be accessed once the lock is held. /// // Our non-atomic increment is safe because we're the only thread /// // which can access the shared state when the lock is held. @@ -57,7 +58,7 @@ use sys_common::mutex as sys; /// tx.send(()); /// } /// // the lock is unlocked here when `data` goes out of scope. -/// }); +/// }).detach(); /// } /// /// rx.recv(); @@ -386,7 +387,7 @@ mod test { fn test_mutex_arc_poison() { let arc = Arc::new(Mutex::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { let lock = arc2.lock(); assert_eq!(*lock, 2); }).join(); @@ -414,7 +415,7 @@ mod test { fn test_mutex_arc_access_in_unwind() { let arc = Arc::new(Mutex::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join::<()>(move|| -> () { + let _ = Thread::spawn(move|| -> () { struct Unwinder { i: Arc>, } diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 1f1e9eea1d635..07b2f2cf541e5 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -409,7 +409,7 @@ mod tests { fn test_rw_arc_poison_wr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { let lock = arc2.write(); assert_eq!(*lock, 2); }).join(); @@ -422,7 +422,7 @@ mod tests { fn test_rw_arc_poison_ww() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { let lock = arc2.write(); assert_eq!(*lock, 2); }).join(); @@ -434,7 +434,7 @@ mod tests { fn test_rw_arc_no_poison_rr() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { let lock = arc2.read(); assert_eq!(*lock, 2); }).join(); @@ -445,7 +445,7 @@ mod tests { fn test_rw_arc_no_poison_rw() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); - let _ = Thread::with_join(move|| { + let _ = Thread::spawn(move|| { let lock = arc2.read(); assert_eq!(*lock, 2); }).join(); @@ -468,13 +468,13 @@ mod tests { *lock = tmp + 1; } tx.send(()); - }); + }).detach(); // Readers try to catch the writer in the act let mut children = Vec::new(); for _ in range(0u, 5) { let arc3 = arc.clone(); - children.push(Thread::with_join(move|| { + children.push(Thread::spawn(move|| { let lock = arc3.read(); assert!(*lock >= 0); })); @@ -495,11 +495,7 @@ mod tests { fn test_rw_arc_access_in_unwind() { let arc = Arc::new(RWLock::new(1i)); let arc2 = arc.clone(); -<<<<<<< HEAD - let _ = task::try(move|| -> () { -======= - let _ = Thread::with_join::<()>(proc() { ->>>>>>> Fallout from new thread API + let _ = Thread::spawn(move|| -> () { struct Unwinder { i: Arc>, } diff --git a/src/libstd/sync/task_pool.rs b/src/libstd/sync/task_pool.rs index 5e7944d5fe507..793825f1b08f5 100644 --- a/src/libstd/sync/task_pool.rs +++ b/src/libstd/sync/task_pool.rs @@ -126,7 +126,7 @@ fn spawn_in_pool(jobs: Arc>>) { } sentinel.cancel(); - }); + }).detach(); } #[cfg(test)] diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 38dc516bf3d81..2ff82bfd7a96b 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -10,8 +10,8 @@ use io::{IoResult, Writer}; use iter::{Iterator, IteratorExt}; -use option::{Some, None}; -use result::{Ok, Err}; +use option::Option::{Some, None}; +use result::Result::{Ok, Err}; use str::{StrPrelude, from_str}; use unicode::char::UnicodeChar; diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 8aa09d9bd3020..421778e20123f 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -82,16 +82,12 @@ impl Helper { *self.signal.get() = send as uint; let t = f(); -<<<<<<< HEAD - task::spawn(move |:| { -======= - Thread::spawn(proc() { ->>>>>>> Fallout from new thread API + Thread::spawn(move |:| { helper(receive, rx, t); let _g = self.lock.lock(); *self.shutdown.get() = true; self.cond.notify_one() - }); + }).detach(); rt::at_exit(move|:| { self.shutdown() }); *self.initialized.get() = true; diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs index 5e1adfb87149f..048e33399a3c4 100644 --- a/src/libstd/sys/common/thread.rs +++ b/src/libstd/sys/common/thread.rs @@ -14,6 +14,7 @@ use boxed::Box; use mem; use uint; use libc; +use thunk::Thunk; use sys_common::stack; use sys::{thread, stack_overflow}; @@ -26,8 +27,8 @@ pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { unsafe { stack::record_os_managed_stack_bounds(0, uint::MAX); let handler = stack_overflow::Handler::new(); - let f: Box = mem::transmute(main); - (*f)(); + let f: Box = mem::transmute(main); + f.invoke(()); drop(handler); mem::transmute(0 as thread::rust_thread_return) } diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index 206d443d2943b..92a896c758332 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -28,6 +28,11 @@ thread_local!(static THREAD_INFO: RefCell> = RefCell::new(Non impl ThreadInfo { fn with(f: |&mut ThreadInfo| -> R) -> R { + if THREAD_INFO.destroyed() { + panic!("Use of std::thread::Thread::current() is not possible after \ + the thread's local data has been destroyed"); + } + THREAD_INFO.with(|c| { if c.borrow().is_none() { *c.borrow_mut() = Some(ThreadInfo { diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index d98f935076fdd..df9dbad2ec787 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -87,8 +87,8 @@ use c_str::CString; use io::{IoResult, Writer}; use libc; use mem; -use option::{Some, None, Option}; -use result::{Ok, Err}; +use option::Option::{mod, Some, None}; +use result::Result::{Ok, Err}; use sync::{StaticMutex, MUTEX_INIT}; use sys_common::backtrace::*; diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index 02da3a1981837..2416b64f98f43 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -16,6 +16,7 @@ use mem; use ptr; use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; use libc; +use thunk::Thunk; use sys_common::stack::RED_ZONE; use sys_common::thread::*; @@ -153,7 +154,7 @@ pub mod guard { } } -pub unsafe fn create(stack: uint, p: Box) -> rust_thread { +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { let mut native: libc::pthread_t = mem::zeroed(); let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(pthread_attr_init(&mut attr), 0); @@ -181,13 +182,13 @@ pub unsafe fn create(stack: uint, p: Box) -> rust_thread { }, }; - let arg: *mut libc::c_void = mem::transmute(p); + let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint let ret = pthread_create(&mut native, &attr, thread_start, arg); assert_eq!(pthread_attr_destroy(&mut attr), 0); if ret != 0 { // be sure to not leak the closure - let _p: Box = mem::transmute(arg); + let _p: Box> = mem::transmute(arg); panic!("failed to spawn native thread: {}", ret); } native diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 9903d2f1ae281..b5961d783ffab 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -27,9 +27,9 @@ use io::{IoResult, Writer}; use libc; use mem; use ops::Drop; -use option::{Some, None}; +use option::Option::{Some, None}; use path::Path; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use sync::{StaticMutex, MUTEX_INIT}; use slice::SliceExt; use str::StrPrelude; diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 05be8de0b56e2..0fb52c758d5db 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -15,7 +15,7 @@ use libc::{mod, c_int}; use c_str::CString; use mem; -use os::windoze::fill_utf16_buf_and_decode; +use sys::os::fill_utf16_buf_and_decode; use path; use ptr; use str; diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 5c690180c44d4..2fbb9494c710b 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -24,6 +24,9 @@ use path::{Path, GenericPath, BytesContainer}; use ptr::{mod, RawPtr}; use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use sys::fs::FileDesc; +use option::Option; +use option::Option::{Some, None}; +use slice; use os::TMPBUF_SZ; use libc::types::os::arch::extra::DWORD; @@ -138,7 +141,7 @@ pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option Option { +unsafe fn get_task_guard_page() -> uint { thread_info::stack_guard() } @@ -55,9 +55,7 @@ extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> L // however stack checks by limit should be disabled on Windows stack::record_sp_limit(0); - if get_task_guard_page().is_some() { - report_overflow(); - } + report_overflow(); EXCEPTION_CONTINUE_SEARCH } diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs index 00f1e9767f5fa..4498f56c00a1e 100644 --- a/src/libstd/sys/windows/thread.rs +++ b/src/libstd/sys/windows/thread.rs @@ -17,6 +17,7 @@ use ptr; use libc; use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, LPVOID, DWORD, LPDWORD, HANDLE}; +use thunk::Thunk; use sys_common::stack::RED_ZONE; use sys_common::thread::*; @@ -43,8 +44,8 @@ pub mod guard { } } -pub unsafe fn create(stack: uint, p: Box) -> rust_thread { - let arg: *mut libc::c_void = mem::transmute(p); +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(box p); // FIXME On UNIX, we guard against stack sizes that are too small but // that's because pthreads enforces that stacks are at least // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's @@ -60,7 +61,7 @@ pub unsafe fn create(stack: uint, p: Box) -> rust_thread { if ret as uint == 0 { // be sure to not leak the closure - let _p: Box = mem::transmute(arg); + let _p: Box = mem::transmute(arg); panic!("failed to spawn native thread: {}", ret); } return ret; diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 6c8d9639d5c4c..0644519aabb45 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -14,8 +14,7 @@ use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use mem; use rt; -use rt::exclusive::Exclusive; -use sync::{ONCE_INIT, Once}; +use sync::{ONCE_INIT, Once, Mutex}; pub type Key = DWORD; pub type Dtor = unsafe extern fn(*mut u8); @@ -55,7 +54,7 @@ pub type Dtor = unsafe extern fn(*mut u8); // /threading/thread_local_storage_win.cc#L42 static INIT_DTORS: Once = ONCE_INIT; -static mut DTORS: *mut Exclusive> = 0 as *mut _; +static mut DTORS: *mut Mutex> = 0 as *mut _; // ------------------------------------------------------------------------- // Native bindings @@ -126,13 +125,13 @@ extern "system" { // FIXME: This could probably be at least a little faster with a BTree. fn init_dtors() { - let dtors = box Exclusive::new(Vec::<(Key, Dtor)>::new()); + let dtors = box Mutex::new(Vec::<(Key, Dtor)>::new()); unsafe { DTORS = mem::transmute(dtors); } rt::at_exit(move|| unsafe { - mem::transmute::<_, Box>>>(DTORS); + mem::transmute::<_, Box>>>(DTORS); DTORS = 0 as *mut _; }); } diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 13395fc9c7637..0f08108fee547 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -17,21 +17,24 @@ use boxed::Box; use thread; use kinds::Send; use result::Result; +use ops::FnOnce; -/// Deprecate: use `std::thread::Cfg` instead. -#[deprecated = "use std::thread::Cfg instead"] -pub type TaskBuilder = thread::Cfg; +/// Deprecate: use `std::thread::Builder` instead. +#[deprecated = "use std::thread::Builder instead"] +pub type TaskBuilder = thread::Builder; -/// Deprecated: use `std::thread::Thread::spawn` instead. -#[deprecated = "use std::thread::Thread::spawn instead"] -pub fn spawn(f: proc(): Send) { - thread::Thread::spawn(f); +/// Deprecated: use `std::thread::Thread::spawn` and `detach` instead. +#[deprecated = "use std::thread::Thread::spawn and detach instead"] +pub fn spawn(f: F) where F: FnOnce(), F: Send { + thread::Thread::spawn(f).detach(); } -/// Deprecated: use `std::thread::Thread::with_join instead`. -#[deprecated = "use std::thread::Thread::with_join instead"] -pub fn try(f: proc(): Send -> T) -> Result> { - thread::Thread::with_join(f).join() +/// Deprecated: use `std::thread::Thread::spawn` and `join` instead. +#[deprecated = "use std::thread::Thread::spawn and join instead"] +pub fn try(f: F) -> Result> where + T: Send, F: FnOnce() -> T, F: Send +{ + thread::Thread::spawn(f).join() } /// Deprecated: use `std::thread::Thread::yield_now instead`. diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index 7ccfe0ab74b46..6c6c0ce85eb04 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -15,13 +15,12 @@ //! An executing Rust program consists of a collection of native OS threads, //! each with their own stack and local state. //! -//! Threads generally have their memory *isolated* from each other by virtue of -//! Rust's owned types (which of course may only be owned by a single thread at -//! a time). Communication between threads can be done through -//! [channels](../../std/comm/index.html), Rust's message-passing types, along -//! with [other forms of thread synchronization](../../std/sync/index.html) and -//! shared-memory data structures. In particular, types that are guaranteed to -//! be threadsafe are easily shared between threads using the +//! Communication between threads can be done through +//! [channels](../../std/comm/index.html), Rust's message-passing +//! types, along with [other forms of thread +//! synchronization](../../std/sync/index.html) and shared-memory data +//! structures. In particular, types that are guaranteed to be +//! threadsafe are easily shared between threads using the //! atomically-reference-counted container, //! [`Arc`](../../std/sync/struct.Arc.html). //! @@ -54,52 +53,43 @@ //! //! ## Spawning a thread //! -//! There are a few different ways to spawn a new thread, depending on how it -//! should relate to the parent thread. -//! -//! ### Simple detached threads -//! -//! The simplest case just spawns a completely independent (detached) thread, -//! returning a new `Thread` handle to it: +//! A new thread can be spawned using the `Thread::spawn` function: //! //! ```rust //! use std::thread::Thread; //! -//! Thread::spawn(proc() { +//! let guard = Thread::spawn(move || { //! println!("Hello, World!"); -//! }) +//! // some computation here +//! }); +//! let result = guard.join(); //! ``` //! -//! The spawned thread may outlive its parent. -//! -//! ### Joining -//! -//! Alternatively, the `with_join` constructor spawns a new thread and returns a -//! `JoinGuard` which can be used to wait until the child thread completes, -//! returning its result (or `Err` if the child thread panicked): -//! -//! ```rust -//! use std::thread::Thread; +//! The `spawn` function doesn't return a `Thread` directly; instead, it returns +//! a *join guard* from which a `Thread` can be extracted. The join guard is an +//! RAII-style guard that will automatically join the child thread (block until +//! it terminates) when it is dropped. You can join the child thread in advance +//! by calling the `join` method on the guard, which will also return the result +//! produced by the thread. //! -//! let guard = Thread::with_join(proc() { panic!() }; -//! assert!(guard.join().is_err()); -//! ``` +//! If you instead wish to *detach* the child thread, allowing it to outlive its +//! parent, you can use the `detach` method on the guard, //! -//! The guard works in RAII style, meaning that the child thread is -//! automatically joined when the guard is dropped. A handle to the thread -//! itself is available via the `thread` method on the guard. +//! A handle to the thread itself is available via the `thread` method on the +//! join guard. //! -//! ### Configured threads +//! ## Configuring threads //! -//! Finally, a new thread can be configured independently of how it is -//! spawned. Configuration is available via the `Cfg` builder, which currently -//! allows you to set the name, stack size, and writers for `println!` and -//! `panic!` for the child thread: +//! A new thread can be configured before it is spawned via the `Builder` type, +//! which currently allows you to set the name, stack size, and writers for +//! `println!` and `panic!` for the child thread: //! //! ```rust //! use std::thread; //! -//! thread::cfg().name("child1").spawn(proc() { println!("Hello, world!") }); +//! thread::Builder::new().name("child1".to_string()).spawn(move || { +//! println!("Hello, world!") +//! }).detach(); //! ``` //! //! ## Blocking support: park and unpark @@ -139,18 +129,19 @@ use core::prelude::*; use any::Any; use borrow::IntoCow; use boxed::Box; -use mem; +use cell::UnsafeCell; use sync::{Mutex, Condvar, Arc}; use string::String; use rt::{mod, unwind}; use io::{Writer, stdio}; +use thunk::Thunk; use sys::thread as imp; use sys_common::{stack, thread_info}; /// Thread configuation. Provides detailed control over the properties /// and behavior of new threads. -pub struct Cfg { +pub struct Builder { // A name for the thread-to-be, for identification in panic messages name: Option, // The size of the stack for the spawned thread @@ -161,11 +152,11 @@ pub struct Cfg { stderr: Option>, } -impl Cfg { +impl Builder { /// Generate the base configuration for spawning a thread, from which /// configuration methods can be chained. - pub fn new() -> Cfg { - Cfg { + pub fn new() -> Builder { + Builder { name: None, stack_size: None, stdout: None, @@ -175,41 +166,51 @@ impl Cfg { /// Name the thread-to-be. Currently the name is used for identification /// only in panic messages. - pub fn name(mut self, name: String) -> Cfg { + pub fn name(mut self, name: String) -> Builder { self.name = Some(name); self } /// Deprecated: use `name` instead #[deprecated = "use name instead"] - pub fn named>(self, name: T) -> Cfg { + pub fn named>(self, name: T) -> Builder { self.name(name.into_cow().into_owned()) } /// Set the size of the stack for the new thread. - pub fn stack_size(mut self, size: uint) -> Cfg { + pub fn stack_size(mut self, size: uint) -> Builder { self.stack_size = Some(size); self } /// Redirect thread-local stdout. #[experimental = "Will likely go away after proc removal"] - pub fn stdout(mut self, stdout: Box) -> Cfg { + pub fn stdout(mut self, stdout: Box) -> Builder { self.stdout = Some(stdout); self } /// Redirect thread-local stderr. #[experimental = "Will likely go away after proc removal"] - pub fn stderr(mut self, stderr: Box) -> Cfg { + pub fn stderr(mut self, stderr: Box) -> Builder { self.stderr = Some(stderr); self } - fn core_spawn(self, f: proc():Send -> T, after: proc(Result):Send) - -> (imp::rust_thread, Thread) + /// Spawn a new joinable thread, and return a JoinGuard guard for it. + /// + /// See `Thead::spawn` and the module doc for more details. + pub fn spawn(self, f: F) -> JoinGuard where + T: Send, F: FnOnce() -> T, F: Send { - let Cfg { name, stack_size, stdout, stderr } = self; + self.spawn_inner(Thunk::new(f)) + } + + fn spawn_inner(self, f: Thunk<(), T>) -> JoinGuard { + let my_packet = Arc::new(UnsafeCell::new(None)); + let their_packet = my_packet.clone(); + + let Builder { name, stack_size, stdout, stderr } = self; let stack_size = stack_size.unwrap_or(rt::min_stack()); let my_thread = Thread::new(name); @@ -221,7 +222,7 @@ impl Cfg { // because by the time that this function is executing we've already // consumed at least a little bit of stack (we don't know the exact byte // address at which our stack started). - let main = proc() { + let main = move |:| { let something_around_the_top_of_the_stack = 1; let addr = &something_around_the_top_of_the_stack as *const int; let my_stack_top = addr as uint; @@ -235,74 +236,49 @@ impl Cfg { their_thread ); - // There are two primary reasons that general try/catch is - // unsafe. The first is that we do not support nested try/catch. The - // fact that this is happening in a newly-spawned thread - // suffices. The second is that unwinding while unwinding is not - // defined. We take care of that by having an 'unwinding' flag in - // the thread itself. For these reasons, this unsafety should be ok. + let mut output = None; + let f: Thunk<(), T> = if stdout.is_some() || stderr.is_some() { + Thunk::new(move |:| { + let _ = stdout.map(stdio::set_stdout); + let _ = stderr.map(stdio::set_stderr); + f.invoke(()) + }) + } else { + f + }; + + let try_result = { + let ptr = &mut output; + + // There are two primary reasons that general try/catch is + // unsafe. The first is that we do not support nested + // try/catch. The fact that this is happening in a newly-spawned + // thread suffices. The second is that unwinding while unwinding + // is not defined. We take care of that by having an + // 'unwinding' flag in the thread itself. For these reasons, + // this unsafety should be ok. + unsafe { + unwind::try(move || *ptr = Some(f.invoke(()))) + } + }; unsafe { - let mut output = None; - let f = if stdout.is_some() || stderr.is_some() { - proc() { - let _ = stdout.map(stdio::set_stdout); - let _ = stderr.map(stdio::set_stderr); - f() - } - } else { - f - }; - - let try_result = { - let ptr = &mut output; - unwind::try(move || *ptr = Some(f())) - }; - match (output, try_result) { - (Some(data), Ok(_)) => after(Ok(data)), - (None, Err(cause)) => after(Err(cause)), + *their_packet.get() = Some(match (output, try_result) { + (Some(data), Ok(_)) => Ok(data), + (None, Err(cause)) => Err(cause), _ => unreachable!() - } + }); } }; - (unsafe { imp::create(stack_size, box main) }, my_thread) - } - - /// Spawn a detached thread, and return a handle to it. - /// - /// The new child thread may outlive its parent. - pub fn spawn(self, f: proc():Send) -> Thread { - let (native, thread) = self.core_spawn(f, proc(_) {}); - unsafe { imp::detach(native) }; - thread - } - - /// Spawn a joinable thread, and return an RAII guard for it. - pub fn with_join(self, f: proc():Send -> T) -> JoinGuard { - // We need the address of the packet to fill in to be stable so when - // `main` fills it in it's still valid, so allocate an extra box to do - // so. - let any: Box = box 0u8; // sentinel value - let my_packet = box Err(any); - let their_packet: *mut Result = unsafe { - *mem::transmute::<&Box>, *const *mut Result>(&my_packet) - }; - - let (native, thread) = self.core_spawn(f, proc(result) { - unsafe { *their_packet = result; } - }); JoinGuard { - native: native, + native: unsafe { imp::create(stack_size, Thunk::new(main)) }, joined: false, - packet: Some(my_packet), - thread: thread, + packet: my_packet, + thread: my_thread, } } } -/// A convenience function for creating configurations. -pub fn cfg() -> Cfg { Cfg::new() } - struct Inner { name: Option, lock: Mutex, // true when there is a buffered unpark @@ -316,6 +292,7 @@ pub struct Thread { } impl Thread { + // Used only internally to construct a thread object without spawning fn new(name: Option) -> Thread { Thread { inner: Arc::new(Inner { @@ -326,16 +303,16 @@ impl Thread { } } - /// Spawn a detached thread, and return a handle to it. + /// Spawn a new joinable thread, returning a `JoinGuard` for it. /// - /// The new child thread may outlive its parent. - pub fn spawn(f: proc():Send) -> Thread { - Cfg::new().spawn(f) - } - - /// Spawn a joinable thread, and return an RAII guard for it. - pub fn with_join(f: proc():Send -> T) -> JoinGuard { - Cfg::new().with_join(f) + /// The join guard can be used to explicitly join the child thead (via + /// `join`), returning `Result`, or it will implicitly join the child + /// upon being dropped. To detach the child, allowing it to outlive the + /// current thread, use `detach`. See the module documentation for additional details. + pub fn spawn(f: F) -> JoinGuard where + T: Send, F: FnOnce() -> T, F: Send + { + Builder::new().spawn(f) } /// Gets a handle to the thread that invokes it. @@ -353,10 +330,15 @@ impl Thread { thread_info::panicking() } - // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp /// Block unless or until the current thread's token is made available (may wake spuriously). /// /// See the module doc for more detail. + // + // The implementation currently uses the trivial strategy of a Mutex+Condvar + // with wakeup flag, which does not actually allow spurious wakeups. In the + // future, this will be implemented in a more efficient way, perhaps along the lines of + // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp + // or futuxes, and in either case may allow spurious wakeups. pub fn park() { let thread = Thread::current(); let mut guard = thread.inner.lock.lock(); @@ -394,43 +376,52 @@ impl thread_info::NewThread for Thread { pub type Result = ::result::Result>; #[must_use] -/// An RAII guard that will block until thread termination when dropped. +/// An RAII-style guard that will block until thread termination when dropped. +/// +/// The type `T` is the return type for the thread's main function. pub struct JoinGuard { native: imp::rust_thread, thread: Thread, joined: bool, - packet: Option>>, + packet: Arc>>>, } impl JoinGuard { /// Extract a handle to the thread this guard will join on. - pub fn thread(&self) -> Thread { - self.thread.clone() + pub fn thread(&self) -> &Thread { + &self.thread } /// Wait for the associated thread to finish, returning the result of the thread's /// calculation. + /// + /// If the child thread panics, `Err` is returned with the parameter given + /// to `panic`. pub fn join(mut self) -> Result { assert!(!self.joined); unsafe { imp::join(self.native) }; self.joined = true; - let box res = self.packet.take().unwrap(); - res + unsafe { + (*self.packet.get()).take().unwrap() + } + } + + /// Detaches the child thread, allowing it to outlive its parent. + pub fn detach(mut self) { + unsafe { imp::detach(self.native) }; + self.joined = true; // avoid joining in the destructor } } #[unsafe_destructor] impl Drop for JoinGuard { fn drop(&mut self) { - // This is required for correctness. If this is not done then the thread - // would fill in a return box which no longer exists. if !self.joined { unsafe { imp::join(self.native) }; } } } -// TODO: fix tests #[cfg(test)] mod test { use any::{Any, AnyRefExt}; @@ -440,21 +431,22 @@ mod test { use result; use std::io::{ChanReader, ChanWriter}; use string::String; - use super::{Thread, cfg}; + use thunk::Thunk; + use super::{Thread, Builder}; // !!! These tests are dangerous. If something is buggy, they will hang, !!! // !!! instead of exiting cleanly. This might wedge the buildbots. !!! #[test] fn test_unnamed_thread() { - Thread::with_join(proc() { + Thread::spawn(move|| { assert!(Thread::current().name().is_none()); }).join().map_err(|_| ()).unwrap(); } #[test] fn test_named_thread() { - cfg().name("ada lovelace".to_string()).with_join(proc() { + Builder::new().name("ada lovelace".to_string()).spawn(move|| { assert!(Thread::current().name().unwrap() == "ada lovelace".to_string()); }).join().map_err(|_| ()).unwrap(); } @@ -462,15 +454,15 @@ mod test { #[test] fn test_run_basic() { let (tx, rx) = channel(); - Thread::spawn(proc() { + Thread::spawn(move|| { tx.send(()); - }); + }).detach(); rx.recv(); } #[test] fn test_join_success() { - match Thread::with_join::(proc() { + match Thread::spawn(move|| -> String { "Success!".to_string() }).join().as_ref().map(|s| s.as_slice()) { result::Result::Ok("Success!") => (), @@ -480,7 +472,7 @@ mod test { #[test] fn test_join_panic() { - match Thread::with_join(proc() { + match Thread::spawn(move|| { panic!() }).join() { result::Result::Err(_) => (), @@ -496,13 +488,13 @@ mod test { fn f(i: int, tx: Sender<()>) { let tx = tx.clone(); - Thread::spawn(proc() { + Thread::spawn(move|| { if i == 0 { tx.send(()); } else { f(i - 1, tx); } - }); + }).detach(); } f(10, tx); @@ -513,25 +505,25 @@ mod test { fn test_spawn_sched_childs_on_default_sched() { let (tx, rx) = channel(); - Thread::spawn(proc() { - Thread::spawn(proc() { + Thread::spawn(move|| { + Thread::spawn(move|| { tx.send(()); - }); - }); + }).detach(); + }).detach(); rx.recv(); } - fn avoid_copying_the_body(spawnfn: |v: proc():Send|) { + fn avoid_copying_the_body(spawnfn: F) where F: FnOnce(Thunk) { let (tx, rx) = channel::(); let x = box 1; let x_in_parent = (&*x) as *const int as uint; - spawnfn(proc() { + spawnfn(Thunk::new(move|| { let x_in_child = (&*x) as *const int as uint; tx.send(x_in_child); - }); + })); let x_in_child = rx.recv(); assert_eq!(x_in_parent, x_in_child); @@ -539,24 +531,25 @@ mod test { #[test] fn test_avoid_copying_the_body_spawn() { - avoid_copying_the_body(|v| { Thread::spawn(v); }); + avoid_copying_the_body(|v| { + Thread::spawn(move || v.invoke(())).detach(); + }); } #[test] fn test_avoid_copying_the_body_thread_spawn() { avoid_copying_the_body(|f| { - let builder = cfg(); - builder.spawn(proc() { - f(); - }); + Thread::spawn(move|| { + f.invoke(()); + }).detach(); }) } #[test] fn test_avoid_copying_the_body_join() { avoid_copying_the_body(|f| { - let _ = Thread::with_join(proc() { - f() + let _ = Thread::spawn(move|| { + f.invoke(()) }).join(); }) } @@ -568,24 +561,24 @@ mod test { // (well, it would if the constant were 8000+ - I lowered it to be more // valgrind-friendly. try this at home, instead..!) static GENERATIONS: uint = 16; - fn child_no(x: uint) -> proc(): Send { - return proc() { + fn child_no(x: uint) -> Thunk { + return Thunk::new(move|| { if x < GENERATIONS { - Thread::spawn(child_no(x+1)); + Thread::spawn(move|| child_no(x+1).invoke(())).detach(); } - } + }); } - Thread::spawn(child_no(0)); + Thread::spawn(|| child_no(0).invoke(())).detach(); } #[test] fn test_simple_newsched_spawn() { - Thread::spawn(proc()()); + Thread::spawn(move || {}).detach(); } #[test] fn test_try_panic_message_static_str() { - match Thread::with_join(proc() { + match Thread::spawn(move|| { panic!("static string"); }).join() { Err(e) => { @@ -599,7 +592,7 @@ mod test { #[test] fn test_try_panic_message_owned_str() { - match Thread::with_join(proc() { + match Thread::spawn(move|| { panic!("owned string".to_string()); }).join() { Err(e) => { @@ -613,7 +606,7 @@ mod test { #[test] fn test_try_panic_message_any() { - match Thread::with_join(proc() { + match Thread::spawn(move|| { panic!(box 413u16 as Box); }).join() { Err(e) => { @@ -631,7 +624,7 @@ mod test { fn test_try_panic_message_unit_struct() { struct Juju; - match Thread::with_join(proc() { + match Thread::spawn(move|| { panic!(Juju) }).join() { Err(ref e) if e.is::() => {} @@ -645,9 +638,9 @@ mod test { let mut reader = ChanReader::new(rx); let stdout = ChanWriter::new(tx); - let r = cfg().stdout(box stdout as Box).with_join(proc() { - print!("Hello, world!"); - }).join(); + let r = Builder::new().stdout(box stdout as Box).spawn(move|| { + print!("Hello, world!"); + }).join(); assert!(r.is_ok()); let output = reader.read_to_string().unwrap(); diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs index 4c052a4ce09f6..4c33d1c418d96 100644 --- a/src/libstd/thread_local/mod.rs +++ b/src/libstd/thread_local/mod.rs @@ -68,6 +68,7 @@ pub mod scoped; /// /// ``` /// use std::cell::RefCell; +/// use std::thread::Thread; /// /// thread_local!(static FOO: RefCell = RefCell::new(1)); /// @@ -77,12 +78,12 @@ pub mod scoped; /// }); /// /// // each thread starts out with the initial value of 1 -/// spawn(move|| { +/// Thread::spawn(move|| { /// FOO.with(|f| { /// assert_eq!(*f.borrow(), 1); /// *f.borrow_mut() = 3; /// }); -/// }); +/// }).detach(); /// /// // we retain our original value of 2 despite the child thread /// FOO.with(|f| { @@ -533,7 +534,7 @@ mod tests { } } - Thread::with_join(move|| { + Thread::spawn(move|| { drop(S1); }).join(); } @@ -551,7 +552,7 @@ mod tests { } } - Thread::with_join(move|| unsafe { + Thread::spawn(move|| unsafe { K1.with(|s| *s.get() = Some(S1)); }).join(); } diff --git a/src/libstd/thunk.rs b/src/libstd/thunk.rs index 42e784959909f..067926042f121 100644 --- a/src/libstd/thunk.rs +++ b/src/libstd/thunk.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// Because this module is temporary... +#![allow(missing_docs)] + use alloc::boxed::Box; use core::kinds::Send; use core::ops::FnOnce; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 044a4a173c692..56af8785a76d4 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -1126,7 +1126,7 @@ pub fn run_test(opts: &TestOpts, let mut reader = ChanReader::new(rx); let stdout = ChanWriter::new(tx.clone()); let stderr = ChanWriter::new(tx); - let mut cfg = thread::cfg().name(match desc.name { + let mut cfg = thread::Builder::new().name(match desc.name { DynTestName(ref name) => name.clone().to_string(), StaticTestName(name) => name.to_string(), }); @@ -1137,11 +1137,11 @@ pub fn run_test(opts: &TestOpts, cfg = cfg.stderr(box stderr as Box); } - let result_guard = cfg.with_join(testfn); + let result_guard = cfg.spawn(move || { testfn.invoke(()) }); let stdout = reader.read_to_end().unwrap().into_iter().collect(); let test_result = calc_result(&desc, result_guard.join()); monitor_ch.send((desc.clone(), test_result, stdout)); - }); + }).detach(); } match testfn { diff --git a/src/test/bench/msgsend-pipes-shared.rs b/src/test/bench/msgsend-pipes-shared.rs index f20fb8d0cd75e..645c029f935a7 100644 --- a/src/test/bench/msgsend-pipes-shared.rs +++ b/src/test/bench/msgsend-pipes-shared.rs @@ -20,7 +20,7 @@ use std::comm; use std::os; -use std::task; +use std::thread::Thread; use std::time::Duration; use std::uint; @@ -64,7 +64,7 @@ fn run(args: &[String]) { let mut worker_results = Vec::new(); for _ in range(0u, workers) { let to_child = to_child.clone(); - worker_results.push(task::try_future(move|| { + worker_results.push(Thread::spawn(move|| { for _ in range(0u, size / workers) { //println!("worker {}: sending {} bytes", i, num_bytes); to_child.send(request::bytes(num_bytes)); @@ -72,12 +72,12 @@ fn run(args: &[String]) { //println!("worker {} exiting", i); })); } - task::spawn(move|| { + Thread::spawn(move|| { server(&from_parent, &to_parent); - }); + }).detach(); for r in worker_results.into_iter() { - r.unwrap(); + let _ = r.join(); } //println!("sending stop message"); diff --git a/src/test/bench/msgsend-pipes.rs b/src/test/bench/msgsend-pipes.rs index 179011be8bf4e..ed96c6406d836 100644 --- a/src/test/bench/msgsend-pipes.rs +++ b/src/test/bench/msgsend-pipes.rs @@ -15,7 +15,7 @@ // I *think* it's the same, more or less. use std::os; -use std::task; +use std::thread::Thread; use std::time::Duration; use std::uint; @@ -58,7 +58,7 @@ fn run(args: &[String]) { let mut worker_results = Vec::new(); let from_parent = if workers == 1 { let (to_child, from_parent) = channel(); - worker_results.push(task::try_future(move|| { + worker_results.push(Thread::spawn(move|| { for _ in range(0u, size / workers) { //println!("worker {}: sending {} bytes", i, num_bytes); to_child.send(request::bytes(num_bytes)); @@ -70,7 +70,7 @@ fn run(args: &[String]) { let (to_child, from_parent) = channel(); for _ in range(0u, workers) { let to_child = to_child.clone(); - worker_results.push(task::try_future(move|| { + worker_results.push(Thread::spawn(move|| { for _ in range(0u, size / workers) { //println!("worker {}: sending {} bytes", i, num_bytes); to_child.send(request::bytes(num_bytes)); @@ -80,12 +80,12 @@ fn run(args: &[String]) { } from_parent }; - task::spawn(move|| { + Thread::spawn(move|| { server(&from_parent, &to_parent); - }); + }).detach(); for r in worker_results.into_iter() { - r.unwrap(); + let _ = r.join(); } //println!("sending stop message"); diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index fae6d2cc3aa65..f7e8fc8fe1b7a 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -22,7 +22,7 @@ extern crate getopts; use std::os; use std::result::Result::{Ok, Err}; -use std::task; +use std::thread::Thread; use std::time::Duration; fn fib(n: int) -> int { @@ -34,15 +34,15 @@ fn fib(n: int) -> int { } else { let (tx1, rx) = channel(); let tx2 = tx1.clone(); - task::spawn(move|| pfib(&tx2, n - 1)); + Thread::spawn(move|| pfib(&tx2, n - 1)).detach(); let tx2 = tx1.clone(); - task::spawn(move|| pfib(&tx2, n - 2)); + Thread::spawn(move|| pfib(&tx2, n - 2)).detach(); tx.send(rx.recv() + rx.recv()); } } let (tx, rx) = channel(); - spawn(move|| pfib(&tx, n) ); + Thread::spawn(move|| pfib(&tx, n) ).detach(); rx.recv() } @@ -77,12 +77,12 @@ fn stress_task(id: int) { fn stress(num_tasks: int) { let mut results = Vec::new(); for i in range(0, num_tasks) { - results.push(task::try_future(move|| { + results.push(Thread::spawn(move|| { stress_task(i); })); } for r in results.into_iter() { - r.unwrap(); + let _ = r.join(); } } diff --git a/src/test/run-fail/main-panic.rs b/src/test/run-fail/main-panic.rs index b750501c265d5..b8ff1be71bf2b 100644 --- a/src/test/run-fail/main-panic.rs +++ b/src/test/run-fail/main-panic.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:task '
' panicked at +// error-pattern:thread '
' panicked at fn main() { panic!() diff --git a/src/test/run-fail/panic-task-name-none.rs b/src/test/run-fail/panic-task-name-none.rs index b363838593209..c943dfe9f0c60 100644 --- a/src/test/run-fail/panic-task-name-none.rs +++ b/src/test/run-fail/panic-task-name-none.rs @@ -8,14 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:task '' panicked at 'test' +// error-pattern:thread '' panicked at 'test' -use std::task; +use std::thread::Thread; fn main() { - let r: Result = task::try(move|| { + let r: Result = Thread::spawn(move|| { panic!("test"); 1i - }); + }).join(); assert!(r.is_ok()); } diff --git a/src/test/run-fail/panic-task-name-owned.rs b/src/test/run-fail/panic-task-name-owned.rs index 3c442861a44a3..57901ebcfc622 100644 --- a/src/test/run-fail/panic-task-name-owned.rs +++ b/src/test/run-fail/panic-task-name-owned.rs @@ -8,15 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:task 'owned name' panicked at 'test' +// error-pattern:thread 'owned name' panicked at 'test' -use std::task::TaskBuilder; +use std::thread::Builder; fn main() { - let r: Result = TaskBuilder::new().named("owned name".to_string()) - .try(move|| { + let r: Result = Builder::new().name("owned name".to_string()).spawn(move|| { panic!("test"); 1i - }); + }).join(); assert!(r.is_ok()); } diff --git a/src/test/run-fail/panic-task-name-send-str.rs b/src/test/run-fail/panic-task-name-send-str.rs deleted file mode 100644 index 92ebb3b4587af..0000000000000 --- a/src/test/run-fail/panic-task-name-send-str.rs +++ /dev/null @@ -1,21 +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. - -// error-pattern:task 'send name' panicked at 'test' - -fn main() { - let r: Result = - ::std::task::TaskBuilder::new().named("send name".into_cow()) - .try(move|| { - panic!("test"); - 3i - }); - assert!(r.is_ok()); -} diff --git a/src/test/run-fail/panic-task-name-static.rs b/src/test/run-fail/panic-task-name-static.rs deleted file mode 100644 index f8dfe9245b438..0000000000000 --- a/src/test/run-fail/panic-task-name-static.rs +++ /dev/null @@ -1,19 +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. - -// error-pattern:task 'static name' panicked at 'test' - -fn main() { - let r: Result = - ::std::task::TaskBuilder::new().named("static name").try(move|| { - panic!("test"); - }); - assert!(r.is_ok()); -} diff --git a/src/test/run-fail/test-panic.rs b/src/test/run-fail/test-panic.rs index cd2ec834d82ac..fa360570253b0 100644 --- a/src/test/run-fail/test-panic.rs +++ b/src/test/run-fail/test-panic.rs @@ -9,7 +9,7 @@ // except according to those terms. // check-stdout -// error-pattern:task 'test_foo' panicked at +// error-pattern:thread 'test_foo' panicked at // compile-flags: --test // ignore-pretty: does not work well with `--test` @@ -17,4 +17,3 @@ fn test_foo() { panic!() } - diff --git a/src/test/run-fail/test-should-fail-bad-message.rs b/src/test/run-fail/test-should-fail-bad-message.rs index 76a5022e3be81..5a5bb53a33a20 100644 --- a/src/test/run-fail/test-should-fail-bad-message.rs +++ b/src/test/run-fail/test-should-fail-bad-message.rs @@ -9,7 +9,7 @@ // except according to those terms. // check-stdout -// error-pattern:task 'test_foo' panicked at +// error-pattern:thread 'test_foo' panicked at // compile-flags: --test // ignore-pretty: does not work well with `--test` @@ -18,5 +18,3 @@ fn test_foo() { panic!("blah") } - - diff --git a/src/test/run-make/bootstrap-from-c-with-native/Makefile b/src/test/run-make/bootstrap-from-c-with-native/Makefile deleted file mode 100644 index 572447097b775..0000000000000 --- a/src/test/run-make/bootstrap-from-c-with-native/Makefile +++ /dev/null @@ -1,12 +0,0 @@ --include ../tools.mk - -HOST_LIB_DIR=$(TMPDIR)/../../../stage$(RUST_BUILD_STAGE)/lib -# This overrides the LD_LIBRARY_PATH for RUN -TARGET_RPATH_DIR:=$(TARGET_RPATH_DIR):$(TMPDIR) - -all: - $(RUSTC) lib.rs - $(CC) main.c -o $(call RUN_BINFILE,main) $(call RPATH_LINK_SEARCH,$(HOST_LIB_DIR)) -lboot $(EXTRACFLAGS) - $(call RUN,main) - $(call REMOVE_DYLIBS,boot) - $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-native/lib.rs b/src/test/run-make/bootstrap-from-c-with-native/lib.rs deleted file mode 100644 index d2fd5393834eb..0000000000000 --- a/src/test/run-make/bootstrap-from-c-with-native/lib.rs +++ /dev/null @@ -1,24 +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. - -#![crate_name="boot"] -#![crate_type="dylib"] - -use std::rt; -use std::thunk::Thunk; - -#[no_mangle] // this needs to get called from C -pub extern "C" fn foo(argc: int, argv: *const *const u8) -> int { - rt::start(argc, argv, Thunk::new(move|| { - spawn(move|| { - println!("hello"); - }); - })) -} diff --git a/src/test/run-make/bootstrap-from-c-with-native/main.c b/src/test/run-make/bootstrap-from-c-with-native/main.c deleted file mode 100644 index 1872c1ea43b11..0000000000000 --- a/src/test/run-make/bootstrap-from-c-with-native/main.c +++ /dev/null @@ -1,16 +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. - -// this is the rust entry point that we're going to call. -int foo(int argc, char *argv[]); - -int main(int argc, char *argv[]) { - return foo(argc, argv); -} diff --git a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs index b5acb55d4a171..440b7afa984f7 100644 --- a/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs +++ b/src/test/run-pass/cleanup-rvalue-temp-during-incomplete-alloc.rs @@ -45,5 +45,5 @@ pub fn fails() { } pub fn main() { - Thread::with_join(fails).join(); + Thread::spawn(fails).join(); } diff --git a/src/test/run-pass/foreign-call-no-runtime.rs b/src/test/run-pass/foreign-call-no-runtime.rs index 0bfae8498e2a6..ce3e447350d40 100644 --- a/src/test/run-pass/foreign-call-no-runtime.rs +++ b/src/test/run-pass/foreign-call-no-runtime.rs @@ -11,7 +11,7 @@ extern crate libc; use std::mem; -use std::rt::thread::Thread; +use std::thread::Thread; #[link(name = "rust_test_helpers")] extern { @@ -21,7 +21,7 @@ extern { pub fn main() { unsafe { - Thread::start(move|| { + Thread::spawn(move|| { let i = &100i; rust_dbg_call(callback, mem::transmute(i)); }).join(); diff --git a/src/test/run-pass/issue-16671.rs b/src/test/run-pass/issue-16671.rs index c695a9e8f9abc..99758f9f777d6 100644 --- a/src/test/run-pass/issue-16671.rs +++ b/src/test/run-pass/issue-16671.rs @@ -18,9 +18,11 @@ // A var moved into a proc, that has a mutable loan path should // not trigger a misleading unused_mut warning. +use std::thread::Thread; + pub fn main() { let mut stdin = std::io::stdin(); - spawn(move|| { + Thread::spawn(move|| { let _ = stdin.read_to_end(); - }); + }).detach(); } diff --git a/src/test/run-pass/issue-2190-1.rs b/src/test/run-pass/issue-2190-1.rs index 3554f2e8f0c31..4bbbe978192b1 100644 --- a/src/test/run-pass/issue-2190-1.rs +++ b/src/test/run-pass/issue-2190-1.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::task::TaskBuilder; +use std::thread::Builder; use std::thunk::Thunk; static generations: uint = 1024+256+128+49; fn spawn(f: Thunk) { - TaskBuilder::new().stack_size(32 * 1024).spawn(move|| f.invoke(())) + Builder::new().stack_size(32 * 1024).spawn(move|| f.invoke(())).detach() } fn child_no(x: uint) -> Thunk { diff --git a/src/test/run-pass/match-ref-binding-in-guard-3256.rs b/src/test/run-pass/match-ref-binding-in-guard-3256.rs index 9a822ddbbcc51..9bb912e081ce6 100644 --- a/src/test/run-pass/match-ref-binding-in-guard-3256.rs +++ b/src/test/run-pass/match-ref-binding-in-guard-3256.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rt::exclusive; +use std::sync::Mutex; pub fn main() { unsafe { - let x = Some(exclusive::Exclusive::new(true)); + let x = Some(Mutex::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock()); diff --git a/src/test/run-pass/running-with-no-runtime.rs b/src/test/run-pass/running-with-no-runtime.rs index 067b0e8dfb4a2..4cf8c52f2bb44 100644 --- a/src/test/run-pass/running-with-no-runtime.rs +++ b/src/test/run-pass/running-with-no-runtime.rs @@ -33,11 +33,11 @@ fn start(argc: int, argv: *const *const u8) -> int { return 0 } - rt::start(argc, argv, Thunk::new(main)) -} - -fn main() { - let args = os::args(); + let args = unsafe { + Vec::from_fn(argc as uint, |i| { + String::from_raw_buf(*argv.offset(i as int)).into_bytes() + }) + }; let me = args[0].as_slice(); let x: &[u8] = &[1u8]; @@ -52,6 +52,8 @@ fn main() { pass(Command::new(me).arg(x).output().unwrap()); let x: &[u8] = &[6u8]; pass(Command::new(me).arg(x).output().unwrap()); + + 0 } fn pass(output: ProcessOutput) { diff --git a/src/test/run-pass/spawning-with-debug.rs b/src/test/run-pass/spawning-with-debug.rs index 0f12827e60ef4..ea594977f905c 100644 --- a/src/test/run-pass/spawning-with-debug.rs +++ b/src/test/run-pass/spawning-with-debug.rs @@ -13,9 +13,9 @@ // regression test for issue #10405, make sure we don't call println! too soon. -use std::task::TaskBuilder; +use std::thread::Builder; pub fn main() { - let mut t = TaskBuilder::new(); - t.spawn(move|| ()); + let mut t = Builder::new(); + t.spawn(move|| ()).detach(); } diff --git a/src/test/run-pass/task-comm-12.rs b/src/test/run-pass/task-comm-12.rs index d950eb8aec450..561c9e91553dd 100644 --- a/src/test/run-pass/task-comm-12.rs +++ b/src/test/run-pass/task-comm-12.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::task; +use std::thread::Thread; pub fn main() { test00(); } @@ -16,19 +16,19 @@ fn start(_task_number: int) { println!("Started / Finished task."); } fn test00() { let i: int = 0; - let mut result = task::try_future(move|| { + let mut result = Thread::spawn(move|| { start(i) }); // Sleep long enough for the task to finish. let mut i = 0u; while i < 10000 { - task::deschedule(); + Thread::yield_now(); i += 1; } // Try joining tasks that have already finished. - result.unwrap(); + result.join(); println!("Joined task."); } diff --git a/src/test/run-pass/task-comm-3.rs b/src/test/run-pass/task-comm-3.rs index 2908ec1e56115..73f6eb563c1ce 100644 --- a/src/test/run-pass/task-comm-3.rs +++ b/src/test/run-pass/task-comm-3.rs @@ -10,7 +10,7 @@ // no-pretty-expanded FIXME #15189 -use std::task; +use std::thread::Thread; pub fn main() { println!("===== WITHOUT THREADS ====="); test00(); } @@ -39,7 +39,7 @@ fn test00() { let mut results = Vec::new(); while i < number_of_tasks { let tx = tx.clone(); - results.push(task::try_future({ + results.push(Thread::spawn({ let i = i; move|| { test00_start(&tx, i, number_of_messages) @@ -60,7 +60,7 @@ fn test00() { } // Join spawned tasks... - for r in results.iter_mut() { r.get_ref(); } + for r in results.into_iter() { r.join(); } println!("Completed: Final number is: "); println!("{}", sum); diff --git a/src/test/run-pass/task-comm-9.rs b/src/test/run-pass/task-comm-9.rs index 1c197e823f2a4..69d70050437f1 100644 --- a/src/test/run-pass/task-comm-9.rs +++ b/src/test/run-pass/task-comm-9.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::task; +use std::thread::Thread; pub fn main() { test00(); } @@ -23,7 +23,7 @@ fn test00() { let (tx, rx) = channel(); let number_of_messages: int = 10; - let result = task::try_future(move|| { + let result = Thread::spawn(move|| { test00_start(&tx, number_of_messages); }); @@ -34,7 +34,7 @@ fn test00() { i += 1; } - result.unwrap(); + result.join(); assert_eq!(sum, number_of_messages * (number_of_messages - 1) / 2); } diff --git a/src/test/run-pass/task-stderr.rs b/src/test/run-pass/task-stderr.rs index 8b54de6b80553..ddeffcdf72204 100644 --- a/src/test/run-pass/task-stderr.rs +++ b/src/test/run-pass/task-stderr.rs @@ -16,7 +16,7 @@ fn main() { let mut reader = ChanReader::new(rx); let stderr = ChanWriter::new(tx); - let res = thread::cfg().stderr(box stderr as Box).with_join(move|| -> () { + let res = thread::Builder::new().stderr(box stderr as Box).spawn(move|| -> () { panic!("Hello, world!") }).join(); assert!(res.is_err()); diff --git a/src/test/run-pass/tcp-stress.rs b/src/test/run-pass/tcp-stress.rs index 4d691dd252656..b3391669d35d0 100644 --- a/src/test/run-pass/tcp-stress.rs +++ b/src/test/run-pass/tcp-stress.rs @@ -19,7 +19,7 @@ extern crate libc; use std::io::net::tcp::{TcpListener, TcpStream}; use std::io::{Acceptor, Listener}; -use std::task::TaskBuilder; +use std::thread::Builder; use std::time::Duration; fn main() { @@ -53,7 +53,7 @@ fn main() { let (tx, rx) = channel(); for _ in range(0u, 1000) { let tx = tx.clone(); - TaskBuilder::new().stack_size(64 * 1024).spawn(move|| { + Builder::new().stack_size(64 * 1024).spawn(move|| { match TcpStream::connect(addr) { Ok(stream) => { let mut stream = stream; @@ -64,7 +64,7 @@ fn main() { Err(e) => debug!("{}", e) } tx.send(()); - }); + }).detach(); } // Wait for all clients to exit, but don't wait for the server to exit. The diff --git a/src/test/run-pass/writealias.rs b/src/test/run-pass/writealias.rs index a86aedd757a30..f48272366e2fd 100644 --- a/src/test/run-pass/writealias.rs +++ b/src/test/run-pass/writealias.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::rt::exclusive; +use std::sync::Mutex; struct Point {x: int, y: int, z: int} @@ -16,7 +16,7 @@ fn f(p: &mut Point) { p.z = 13; } pub fn main() { unsafe { - let x = Some(exclusive::Exclusive::new(true)); + let x = Some(Mutex::new(true)); match x { Some(ref z) if *z.lock() => { assert!(*z.lock()); diff --git a/src/test/run-pass/yield.rs b/src/test/run-pass/yield.rs index 89d204dcecbdb..9a96b483f2c92 100644 --- a/src/test/run-pass/yield.rs +++ b/src/test/run-pass/yield.rs @@ -8,18 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::task; +use std::thread::Thread; pub fn main() { - let mut result = task::try_future(child); + let mut result = Thread::spawn(child); println!("1"); - task::deschedule(); + Thread::yield_now(); println!("2"); - task::deschedule(); + Thread::yield_now(); println!("3"); - result.unwrap(); + result.join(); } fn child() { - println!("4"); task::deschedule(); println!("5"); task::deschedule(); println!("6"); + println!("4"); Thread::yield_now(); println!("5"); Thread::yield_now(); println!("6"); } diff --git a/src/test/run-pass/yield1.rs b/src/test/run-pass/yield1.rs index d882b1abd295d..13119e5d909cd 100644 --- a/src/test/run-pass/yield1.rs +++ b/src/test/run-pass/yield1.rs @@ -8,13 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::task; +use std::thread::Thread; pub fn main() { - let mut result = task::try_future(child); + let mut result = Thread::spawn(child); println!("1"); - task::deschedule(); - result.unwrap(); + Thread::yield_now(); + result.join(); } fn child() { println!("2"); } From 5759cff48e66bcf2bf2cf821211bdf683292d8f3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Dec 2014 14:37:38 -0800 Subject: [PATCH 19/24] std: Lower abstractions for thread_local/at_exit The current implementations use `std::sync` primitives, but these primitives currently end up relying on `thread_info` and a local `Thread` being available (mainly for checking the panicking flag). To get around this, this commit lowers the abstractions used by the windows thread_local implementation as well as the at_exit_imp module. Both of these modules now use a `sys::Mutex` and a `static mut` and manage the allocation/locking manually. --- src/libstd/rt/at_exit_imp.rs | 62 +++++++++++++++----------- src/libstd/sys/windows/thread_local.rs | 62 +++++++++++++++++--------- 2 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs index b63b4ced00545..5823f8453d84a 100644 --- a/src/libstd/rt/at_exit_imp.rs +++ b/src/libstd/rt/at_exit_imp.rs @@ -16,35 +16,49 @@ use core::prelude::*; use boxed::Box; use vec::Vec; -use sync::{Mutex, atomic, Once, ONCE_INIT}; use mem; use thunk::Thunk; +use sys_common::mutex::{Mutex, MUTEX_INIT}; -type Queue = Mutex>; +type Queue = Vec; -static INIT: Once = ONCE_INIT; -static QUEUE: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static LOCK: Mutex = MUTEX_INIT; +static mut QUEUE: *mut Queue = 0 as *mut Queue; -fn init() { - let state: Box = box Mutex::new(Vec::new()); - unsafe { - QUEUE.store(mem::transmute(state), atomic::SeqCst); - - // FIXME: switch this to use atexit as below. Currently this - // segfaults (the queue's memory is mysteriously gone), so - // instead the cleanup is tied to the `std::rt` entry point. - // - // ::libc::atexit(cleanup); +unsafe fn init() { + if QUEUE.is_null() { + let state: Box = box Vec::new(); + QUEUE = mem::transmute(state); + } else { + // can't re-init after a cleanup + rtassert!(QUEUE as uint != 1); } + + // FIXME: switch this to use atexit as below. Currently this + // segfaults (the queue's memory is mysteriously gone), so + // instead the cleanup is tied to the `std::rt` entry point. + // + // ::libc::atexit(cleanup); } pub fn cleanup() { unsafe { - let queue = QUEUE.swap(0, atomic::SeqCst); - if queue != 0 { + LOCK.lock(); + let queue = QUEUE; + QUEUE = 1 as *mut _; + LOCK.unlock(); + + // make sure we're not recursively cleaning up + rtassert!(queue as uint != 1); + + // If we never called init, not need to cleanup! + if queue as uint != 0 { let queue: Box = mem::transmute(queue); - let v = mem::replace(&mut *queue.lock(), Vec::new()); - for to_run in v.into_iter() { + for to_run in queue.into_iter() { to_run.invoke(()); } } @@ -52,14 +66,10 @@ pub fn cleanup() { } pub fn push(f: Thunk) { - INIT.doit(init); 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! - let queue = QUEUE.load(atomic::SeqCst); - rtassert!(queue != 0); - (*(queue as *const Queue)).lock().push(f); + LOCK.lock(); + init(); + (*QUEUE).push(f); + LOCK.unlock(); } } diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs index 0644519aabb45..60b0d584db3a7 100644 --- a/src/libstd/sys/windows/thread_local.rs +++ b/src/libstd/sys/windows/thread_local.rs @@ -14,7 +14,7 @@ use libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; use mem; use rt; -use sync::{ONCE_INIT, Once, Mutex}; +use sys_common::mutex::{MUTEX_INIT, Mutex}; pub type Key = DWORD; pub type Dtor = unsafe extern fn(*mut u8); @@ -53,8 +53,12 @@ pub type Dtor = unsafe extern fn(*mut u8); // [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base // /threading/thread_local_storage_win.cc#L42 -static INIT_DTORS: Once = ONCE_INIT; -static mut DTORS: *mut Mutex> = 0 as *mut _; +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static DTOR_LOCK: Mutex = MUTEX_INIT; +static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _; // ------------------------------------------------------------------------- // Native bindings @@ -124,30 +128,40 @@ extern "system" { // // FIXME: This could probably be at least a little faster with a BTree. -fn init_dtors() { - let dtors = box Mutex::new(Vec::<(Key, Dtor)>::new()); - unsafe { - DTORS = mem::transmute(dtors); - } +unsafe fn init_dtors() { + if !DTORS.is_null() { return } + + let dtors = box Vec::<(Key, Dtor)>::new(); + DTORS = mem::transmute(dtors); - rt::at_exit(move|| unsafe { - mem::transmute::<_, Box>>>(DTORS); + rt::at_exit(move|| { + DTOR_LOCK.lock(); + let dtors = DTORS; DTORS = 0 as *mut _; + mem::transmute::<_, Box>>(dtors); + assert!(DTORS.is_null()); // can't re-init after destructing + DTOR_LOCK.unlock(); }); } unsafe fn register_dtor(key: Key, dtor: Dtor) { - INIT_DTORS.doit(init_dtors); - let mut dtors = (*DTORS).lock(); - dtors.push((key, dtor)); + DTOR_LOCK.lock(); + init_dtors(); + (*DTORS).push((key, dtor)); + DTOR_LOCK.unlock(); } unsafe fn unregister_dtor(key: Key) -> bool { - if DTORS.is_null() { return false } - let mut dtors = (*DTORS).lock(); - let before = dtors.len(); - dtors.retain(|&(k, _)| k != key); - dtors.len() != before + DTOR_LOCK.lock(); + init_dtors(); + let ret = { + let dtors = &mut *DTORS; + let before = dtors.len(); + dtors.retain(|&(k, _)| k != key); + dtors.len() != before + }; + DTOR_LOCK.unlock(); + ret } // ------------------------------------------------------------------------- @@ -219,12 +233,20 @@ unsafe extern "system" fn on_tls_callback(h: LPVOID, } unsafe fn run_dtors() { - if DTORS.is_null() { return } let mut any_run = true; for _ in range(0, 5i) { if !any_run { break } any_run = false; - let dtors = (*DTORS).lock().iter().map(|p| *p).collect::>(); + let dtors = { + DTOR_LOCK.lock(); + let ret = if DTORS.is_null() { + Vec::new() + } else { + (*DTORS).iter().map(|s| *s).collect() + }; + DTOR_LOCK.unlock(); + ret + }; for &(key, dtor) in dtors.iter() { let ptr = TlsGetValue(key); if !ptr.is_null() { From d08600b189eeb2e61879b44a07f9fdc33fa82689 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Dec 2014 14:59:20 -0800 Subject: [PATCH 20/24] std: Move the panic flag to its own thread local This flag is somewhat tied to the `unwind` module rather than the `thread_info` module, so this commit moves it into that module as well as allowing the same OS thread to call `unwind::try` multiple times. Previously once a thread panicked its panic flag was never reset, even after exiting the panic handler. --- src/libstd/failure.rs | 16 +++++----------- src/libstd/rt/unwind.rs | 19 +++++++++++++++---- src/libstd/sys/common/thread_info.rs | 11 ----------- src/libstd/thread.rs | 2 +- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 3934b0cf45f0c..8e1e3dc4af922 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -10,21 +10,15 @@ #![experimental] -use alloc::boxed::Box; +use prelude::*; + use any::{Any, AnyRefExt}; use cell::RefCell; use fmt; -use io::{Writer, IoResult}; -use kinds::Send; -use option::Option; -use option::Option::{Some, None}; -use result::Result::Ok; -use rt::backtrace; +use io::IoResult; +use rt::{backtrace, unwind}; use rt::util::{Stderr, Stdio}; -use str::Str; -use string::String; use thread::Thread; -use sys_common::thread_info; // Defined in this module instead of io::stdio so that the unwinding thread_local! { @@ -80,7 +74,7 @@ pub fn on_fail(obj: &(Any+Send), file: &'static str, line: uint) { // If this is a double panic, make sure that we printed a backtrace // for this panic. - if thread_info::panicking() && !backtrace::log_enabled() { + if unwind::panicking() && !backtrace::log_enabled() { let _ = backtrace::write(&mut err); } } diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 8ef10cbbd77db..9f34a72f80779 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -60,6 +60,7 @@ use prelude::*; use any::Any; +use cell::Cell; use cmp; use failure; use fmt; @@ -69,7 +70,6 @@ use mem; use sync::atomic; use sync::{Once, ONCE_INIT}; -use sys_common::thread_info; use rt::libunwind as uw; struct Exception { @@ -94,6 +94,8 @@ static CALLBACKS: [atomic::AtomicUint, ..MAX_CALLBACKS] = atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT]; static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; +thread_local!(static PANICKING: Cell = Cell::new(false)) + /// Invoke a closure, capturing the cause of panic if one occurs. /// /// This function will return `None` if the closure did not panic, and will @@ -116,7 +118,11 @@ static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; /// run. pub unsafe fn try(f: F) -> Result<(), Box> { let mut f = Some(f); + + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); let ep = rust_try(try_fn::, &mut f as *mut _ as *mut c_void); + PANICKING.with(|s| s.set(prev)); return if ep.is_null() { Ok(()) } else { @@ -146,6 +152,11 @@ pub unsafe fn try(f: F) -> Result<(), Box> { } } +/// Test if the current thread is currently panicking. +pub fn panicking() -> bool { + PANICKING.with(|s| s.get()) +} + // An uninlined, unmangled function upon which to slap yer breakpoints #[inline(never)] #[no_mangle] @@ -561,15 +572,15 @@ fn begin_unwind_inner(msg: Box, file_line: &(&'static str, uint)) -> // Now that we've run all the necessary unwind callbacks, we actually // perform the unwinding. - if thread_info::panicking() { + if panicking() { // If a thread panics 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."); + rterrln!("thread panicked while panicking. aborting."); unsafe { intrinsics::abort() } } - thread_info::set_unwinding(true); + PANICKING.with(|s| s.set(true)); rust_panic(msg); } diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index 92a896c758332..0519bc56f0a81 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -20,7 +20,6 @@ struct ThreadInfo { // hence this is optional. stack_bounds: (uint, uint), stack_guard: uint, - unwinding: bool, thread: Thread, } @@ -38,7 +37,6 @@ impl ThreadInfo { *c.borrow_mut() = Some(ThreadInfo { stack_bounds: (0, 0), stack_guard: 0, - unwinding: false, thread: NewThread::new(None), }) } @@ -51,24 +49,15 @@ pub fn current_thread() -> Thread { ThreadInfo::with(|info| info.thread.clone()) } -pub fn panicking() -> bool { - ThreadInfo::with(|info| info.unwinding) -} - pub fn stack_guard() -> uint { ThreadInfo::with(|info| info.stack_guard) } -pub fn set_unwinding(unwinding: bool) { - ThreadInfo::with(|info| info.unwinding = unwinding) -} - pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { THREAD_INFO.with(|c| assert!(c.borrow().is_none())); THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ stack_bounds: stack_bounds, stack_guard: stack_guard, - unwinding: false, thread: thread, })); } diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs index 6c6c0ce85eb04..8ef53a22aeb04 100644 --- a/src/libstd/thread.rs +++ b/src/libstd/thread.rs @@ -327,7 +327,7 @@ impl Thread { /// Determines whether the current thread is panicking. pub fn panicking() -> bool { - thread_info::panicking() + unwind::panicking() } /// Block unless or until the current thread's token is made available (may wake spuriously). From 0ce5faaa7becfac2786c4a305602b63dc96ba2f8 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Wed, 17 Dec 2014 15:41:16 -0800 Subject: [PATCH 21/24] Delete rest of rustrt ... and address other rebasing fallout. --- src/librustrt/lib.rs | 132 ----------------------------------------- src/libstd/comm/mod.rs | 2 +- src/libstd/io/stdio.rs | 1 + 3 files changed, 2 insertions(+), 133 deletions(-) delete mode 100644 src/librustrt/lib.rs diff --git a/src/librustrt/lib.rs b/src/librustrt/lib.rs deleted file mode 100644 index 02ca7d3ce6d45..0000000000000 --- a/src/librustrt/lib.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![crate_name = "rustrt"] -#![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/nightly/")] - -#![allow(unknown_features)] -#![feature(macro_rules, phase, globs, thread_local, asm)] -#![feature(linkage, lang_items, unsafe_destructor, default_type_params)] -#![feature(import_shadowing, slicing_syntax)] -#![feature(unboxed_closures)] -#![no_std] -#![experimental] - -#[phase(plugin, link)] extern crate core; -extern crate alloc; -extern crate libc; -extern crate collections; - -#[cfg(test)] extern crate "rustrt" as realrustrt; -#[cfg(test)] extern crate test; - -#[cfg(test)] #[phase(plugin, link)] extern crate std; - -pub use self::util::{Stdio, Stdout, Stderr}; -pub use self::unwind::{begin_unwind, begin_unwind_fmt}; - -use core::prelude::*; - -mod macros; - -mod at_exit_imp; -mod local_ptr; -mod thread_local_storage; -mod util; -mod libunwind; -mod stack_overflow; -pub mod thunk; - -pub mod args; -pub mod bookkeeping; -pub mod c_str; -pub mod exclusive; -pub mod local; -pub mod mutex; -pub mod stack; -pub mod task; -pub mod thread; -pub mod unwind; - -/// The default error code of the rust runtime if the main task panics instead -/// of exiting cleanly. -pub const DEFAULT_ERROR_CODE: int = 101; - -/// One-time runtime initialization. -/// -/// Initializes global state, including frobbing the crate's logging flags, -/// and storing the process arguments. -pub fn init(argc: int, argv: *const *const 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(); - thread::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(); - libc::issue_14344_workaround(); -} - -/// 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: F) { - at_exit_imp::push(thunk::Thunk::new(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(); - thread::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(all(not(windows), not(target_os = "android"), not(target_os = "ios")))] - pub use super::local_ptr::compiled::RT_TLS_PTR; -} - -#[cfg(not(test))] -mod std { - pub use core::{fmt, option, cmp, kinds}; -} diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 4977f966eba29..8f945fec4d562 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -1516,7 +1516,7 @@ mod test { // wait for the child task to exit before we exit rx2.recv(); - }) + }} } #[cfg(test)] diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 8e28ad1add58a..aa50597c81698 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -41,6 +41,7 @@ use option::Option; use option::Option::{Some, None}; use ops::{Deref, DerefMut, FnOnce}; use result::Result::{Ok, Err}; +use rt; use slice::SliceExt; use str::StrPrelude; use string::String; From f4c0c0ff42e81a47c4e1e6985db62a28a8576406 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 18 Dec 2014 18:56:28 -0800 Subject: [PATCH 22/24] Disable stack overflow test on android, which seems to be failing spuriously. cc https://github.com/rust-lang/rust/issues/20004 --- src/test/run-pass/out-of-stack.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/run-pass/out-of-stack.rs b/src/test/run-pass/out-of-stack.rs index 7f2f9f9ece83d..81e75ba2cd53d 100644 --- a/src/test/run-pass/out-of-stack.rs +++ b/src/test/run-pass/out-of-stack.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-android: FIXME (#20004) + #![feature(asm)] use std::io::process::Command; From a9e7669cdc551588144a5b56db0b42dd35b71876 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 18 Dec 2014 19:41:20 -0800 Subject: [PATCH 23/24] Rebasing fixes. --- src/libstd/rt/unwind.rs | 2 +- src/libstd/sys/common/backtrace.rs | 28 ++++++++++++++++++---------- src/libstd/sys/common/thread_info.rs | 2 +- src/libstd/sys/windows/backtrace.rs | 4 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 9f34a72f80779..5d5440b887d89 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -94,7 +94,7 @@ static CALLBACKS: [atomic::AtomicUint, ..MAX_CALLBACKS] = atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT]; static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; -thread_local!(static PANICKING: Cell = Cell::new(false)) +thread_local! { static PANICKING: Cell = Cell::new(false) } /// Invoke a closure, capturing the cause of panic if one occurs. /// diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 2ff82bfd7a96b..a39c8d6d8fed8 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -40,8 +40,18 @@ pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { // expecting, we just print it literally. Note that we must handle non-rust // symbols because we could have any function in the backtrace. let mut valid = true; + let mut inner = s; if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { - let mut chars = s.slice(3, s.len() - 1).chars(); + inner = s.slice(3, s.len() - 1); + // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. + } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { + inner = s.slice(2, s.len() - 1); + } else { + valid = false; + } + + if valid { + let mut chars = inner.chars(); while valid { let mut i = 0; for c in chars { @@ -58,32 +68,29 @@ pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { valid = false; } } - } else { - valid = false; } // Alright, let's do this. if !valid { try!(writer.write_str(s)); } else { - let mut s = s.slice_from(3); let mut first = true; - while s.len() > 1 { + while inner.len() > 0 { if !first { try!(writer.write_str("::")); } else { first = false; } - let mut rest = s; + let mut rest = inner; while rest.char_at(0).is_numeric() { rest = rest.slice_from(1); } - let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap(); - s = rest.slice_from(i); + let i: uint = from_str(inner.slice_to(inner.len() - rest.len())).unwrap(); + inner = rest.slice_from(i); rest = rest.slice_to(i); while rest.len() > 0 { if rest.starts_with("$") { - macro_rules! demangle( + macro_rules! demangle { ($($pat:expr => $demangled:expr),*) => ({ $(if rest.starts_with($pat) { try!(writer.write_str($demangled)); @@ -95,7 +102,8 @@ pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { } }) - ) + } + // see src/librustc/back/link.rs for these mappings demangle! ( "$SP$" => "@", diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs index 0519bc56f0a81..dc21feb17a8e8 100644 --- a/src/libstd/sys/common/thread_info.rs +++ b/src/libstd/sys/common/thread_info.rs @@ -23,7 +23,7 @@ struct ThreadInfo { thread: Thread, } -thread_local!(static THREAD_INFO: RefCell> = RefCell::new(None)) +thread_local! { static THREAD_INFO: RefCell> = RefCell::new(None) } impl ThreadInfo { fn with(f: |&mut ThreadInfo| -> R) -> R { diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index b5961d783ffab..f2f543dd9697f 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -304,12 +304,12 @@ pub fn write(w: &mut Writer) -> IoResult<()> { Err(..) => return Ok(()), }; - macro_rules! sym( ($e:expr, $t:ident) => (unsafe { + macro_rules! sym{ ($e:expr, $t:ident) => (unsafe { match lib.symbol($e) { Ok(f) => mem::transmute::<*mut u8, $t>(f), Err(..) => return Ok(()) } - }) ) + }) } // Fetch the symbols necessary from dbghelp.dll let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); From 903c5a8f69714382ec9fc22745f902c3e219cb68 Mon Sep 17 00:00:00 2001 From: Aaron Turon Date: Thu, 18 Dec 2014 23:29:30 -0800 Subject: [PATCH 24/24] Disable at_exit handlers The [final step](https://github.com/rust-lang/rust/pull/19654) of runtime removal changes the threading/process model so that the process shuts down when the main thread exits. But several shared resources, like the helper thread for timeouts, are shut down when the main thread exits (but before the process ends), and they are not prepared to be used after shut down, but other threads may try to access them during the shutdown sequence of the main thread. As an interim solution, the `at_exit` cleanup routine is simply skipped. Ultimately, these resources should be made to safely handle asynchronous shutdown, usually by panicking if called from a detached thread when the main thread is ending. See issue for details https://github.com/rust-lang/rust/issues/20012 This is a [breaking-change] for anyone relying on `at_exit`. --- src/libstd/rt/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index fd50d845716b9..8d9c1268e7e33 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -162,5 +162,8 @@ pub fn at_exit(f: F) { pub unsafe fn cleanup() { args::cleanup(); sys::stack_overflow::cleanup(); - at_exit_imp::cleanup(); + // FIXME: (#20012): the resources being cleaned up by at_exit + // currently are not prepared for cleanup to happen asynchronously + // with detached threads using the resources; for now, we leak. + // at_exit_imp::cleanup(); }