From 4c624865dd4c69f9f0ec8975c6225afd3094dfd4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Jun 2014 16:03:27 -0700 Subject: [PATCH 1/3] rustc: Allow safe access of shareable static muts Taking a shareable loan of a `static mut` is safe if the type contained in the location is ascribes to `Share`. This commit removes the need to have an `unsafe` block in these situations. Unsafe blocks are still required to write to or take mutable loans out of statics. An example of code that no longer requires unsafe code is: use std::sync::atomics; static mut CNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; let cnt = CNT.fetch_add(1, atomics::SeqCst); As a consequence of this change, this code is not permitted: static mut FOO: uint = 30; println!("{}", FOO); The type `uint` is `Share`, and the static only has a shareable loan taken out on it, so the access does not require an unsafe block. Writes to the static are still unsafe, however, as they can invoke undefined behavior if not properly synchronized. Closes #13232 --- src/librustc/middle/effect.rs | 111 +++++++++++++++++- .../static-mut-foreign-requires-unsafe.rs | 1 - .../compile-fail/static-mut-needs-unsafe.rs | 66 +++++++++++ .../static-mut-requires-unsafe.rs | 1 - .../run-pass/static-mut-no-need-unsafe.rs | 39 ++++++ 5 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/test/compile-fail/static-mut-needs-unsafe.rs create mode 100644 src/test/run-pass/static-mut-no-need-unsafe.rs diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs index e6160038b1d8c..03466c49d13a6 100644 --- a/src/librustc/middle/effect.rs +++ b/src/librustc/middle/effect.rs @@ -15,6 +15,9 @@ use middle::def; use middle::ty; use middle::typeck::MethodCall; use util::ppaux; +use util::nodemap::NodeSet; +use euv = middle::expr_use_visitor; +use mc = middle::mem_categorization; use syntax::ast; use syntax::ast_util::PostExpansionMethod; @@ -40,10 +43,18 @@ fn type_is_unsafe_function(ty: ty::t) -> bool { struct EffectCheckVisitor<'a> { tcx: &'a ty::ctxt, + mutably_accessed_statics: &'a mut NodeSet, + /// Whether we're in an unsafe context. unsafe_context: UnsafeContext, } +struct FunctionVisitor<'a, 'b>(euv::ExprUseVisitor<'a, 'b, ty::ctxt>); + +struct StaticMutChecker<'a> { + mutably_accessed_statics: NodeSet, +} + impl<'a> EffectCheckVisitor<'a> { fn require_unsafe(&mut self, span: Span, description: &str) { match self.unsafe_context { @@ -142,6 +153,10 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { } fn visit_expr(&mut self, expr: &ast::Expr, _:()) { + if self.mutably_accessed_statics.remove(&expr.id) { + self.require_unsafe(expr.span, "mutable use of static") + } + match expr.node { ast::ExprMethodCall(_, _, _) => { let method_call = MethodCall::expr(expr.id); @@ -185,7 +200,12 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { ast::ExprPath(..) => { match ty::resolve_expr(self.tcx, expr) { def::DefStatic(_, true) => { - self.require_unsafe(expr.span, "use of mutable static") + let ty = ty::node_id_to_type(self.tcx, expr.id); + let contents = ty::type_contents(self.tcx, ty); + if !contents.is_sharable(self.tcx) { + self.require_unsafe(expr.span, + "use of non-Share static mut") + } } _ => {} } @@ -197,11 +217,98 @@ impl<'a> Visitor<()> for EffectCheckVisitor<'a> { } } +impl<'a, 'b> Visitor<()> for FunctionVisitor<'a, 'b> { + fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl, + b: &ast::Block, s: Span, _: ast::NodeId, _: ()) { + { + let FunctionVisitor(ref mut inner) = *self; + inner.walk_fn(fd, b); + } + visit::walk_fn(self, fk, fd, b, s, ()); + } +} + +impl<'a> StaticMutChecker<'a> { + fn is_static_mut(&self, mut cur: &mc::cmt) -> bool { + loop { + match cur.cat { + mc::cat_static_item => { + return match cur.mutbl { + mc::McImmutable => return false, + _ => true + } + } + mc::cat_deref(ref cmt, _, _) | + mc::cat_discr(ref cmt, _) | + mc::cat_downcast(ref cmt) | + mc::cat_interior(ref cmt, _) => cur = cmt, + + mc::cat_rvalue(..) | + mc::cat_copied_upvar(..) | + mc::cat_upvar(..) | + mc::cat_local(..) | + mc::cat_arg(..) => return false + } + } + } +} + +impl<'a> euv::Delegate for StaticMutChecker<'a> { + fn borrow(&mut self, + borrow_id: ast::NodeId, + _borrow_span: Span, + cmt: mc::cmt, + _loan_region: ty::Region, + bk: ty::BorrowKind, + _loan_cause: euv::LoanCause) { + if !self.is_static_mut(&cmt) { + return + } + match bk { + ty::ImmBorrow => {} + ty::UniqueImmBorrow | ty::MutBorrow => { + self.mutably_accessed_statics.insert(borrow_id); + } + } + } + + fn mutate(&mut self, + assignment_id: ast::NodeId, + _assignment_span: Span, + assignee_cmt: mc::cmt, + _mode: euv::MutateMode) { + if !self.is_static_mut(&assignee_cmt) { + return + } + self.mutably_accessed_statics.insert(assignment_id); + } + + fn consume(&mut self, + _consume_id: ast::NodeId, + _consume_span: Span, + _cmt: mc::cmt, + _mode: euv::ConsumeMode) {} + fn consume_pat(&mut self, + _consume_pat: &ast::Pat, + _cmt: mc::cmt, + _mode: euv::ConsumeMode) {} + fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {} +} + pub fn check_crate(tcx: &ty::ctxt, krate: &ast::Crate) { + let mut delegate = StaticMutChecker { + mutably_accessed_statics: NodeSet::new(), + }; + { + let visitor = euv::ExprUseVisitor::new(&mut delegate, tcx); + visit::walk_crate(&mut FunctionVisitor(visitor), krate, ()); + } + let mut visitor = EffectCheckVisitor { tcx: tcx, unsafe_context: SafeContext, + mutably_accessed_statics: &mut delegate.mutably_accessed_statics, }; - visit::walk_crate(&mut visitor, krate, ()); + assert!(visitor.mutably_accessed_statics.len() == 0); } diff --git a/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs b/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs index 0e44af19a7f62..2ce096d6a1509 100644 --- a/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs +++ b/src/test/compile-fail/static-mut-foreign-requires-unsafe.rs @@ -17,5 +17,4 @@ extern { fn main() { a += 3; //~ ERROR: requires unsafe a = 4; //~ ERROR: requires unsafe - let _b = a; //~ ERROR: requires unsafe } diff --git a/src/test/compile-fail/static-mut-needs-unsafe.rs b/src/test/compile-fail/static-mut-needs-unsafe.rs new file mode 100644 index 0000000000000..72d4b9ffba059 --- /dev/null +++ b/src/test/compile-fail/static-mut-needs-unsafe.rs @@ -0,0 +1,66 @@ +// 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 std::kinds::marker; + +struct NonSharable { + field: uint, + noshare: marker::NoShare +} + +struct Sharable { + field: uint +} + +impl Sharable { + fn foo(&self) {} + fn foo_mut(&mut self) {} +} + +static mut NON_SHARABLE: NonSharable = NonSharable { + field: 1, + noshare: marker::NoShare, +}; + +static mut SHARABLE: Sharable = Sharable { field: 0 }; + +pub fn fn_mut(_: &mut Sharable) {} + +pub fn main() { + SHARABLE.foo(); + + SHARABLE.foo_mut(); + //~^ ERROR: mutable use of static requires unsafe function or block + + SHARABLE.field = 2; + //~^ ERROR: mutable use of static requires unsafe function or block + + fn_mut(&mut SHARABLE); + //~^ ERROR mutable use of static requires unsafe function or block + + NON_SHARABLE.field = 2; + //~^ ERROR: use of non-Share static mut requires unsafe function or block + //~^^ ERROR: mutable use of static requires unsafe function or block + + SHARABLE = Sharable {field: 1}; + //~^ ERROR: mutable use of static requires unsafe function or block + + let _: &mut Sharable = &mut SHARABLE; + //~^ ERROR mutable use of static requires unsafe function or block + + let _ = &NON_SHARABLE.field; + //~^ ERROR: use of non-Share static mut requires unsafe function or block + + let mut slc = ['a', 'c']; + slc[NON_SHARABLE.field] = 'b'; + //~^ ERROR: use of non-Share static mut requires unsafe function or block + + slc[SHARABLE.field] = 'b'; +} diff --git a/src/test/compile-fail/static-mut-requires-unsafe.rs b/src/test/compile-fail/static-mut-requires-unsafe.rs index 7337920cce68c..cd3c67a8930da 100644 --- a/src/test/compile-fail/static-mut-requires-unsafe.rs +++ b/src/test/compile-fail/static-mut-requires-unsafe.rs @@ -13,5 +13,4 @@ static mut a: int = 3; fn main() { a += 3; //~ ERROR: requires unsafe a = 4; //~ ERROR: requires unsafe - let _b = a; //~ ERROR: requires unsafe } diff --git a/src/test/run-pass/static-mut-no-need-unsafe.rs b/src/test/run-pass/static-mut-no-need-unsafe.rs new file mode 100644 index 0000000000000..21c46186c31aa --- /dev/null +++ b/src/test/run-pass/static-mut-no-need-unsafe.rs @@ -0,0 +1,39 @@ +// 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 Sharable { + field: uint +} + +impl Sharable { + fn foo(&self) {} + fn foo_mut(&mut self) {} +} + +static mut FOO: Sharable = Sharable { field: 1 }; + +fn borrow_static(_: &Sharable) {} + +pub fn main() { + + FOO.foo(); + + borrow_static(&FOO); + + let _ = &FOO; + + unsafe { let _: &mut Sharable = &mut FOO; } + + let mut slc = ['a', 'c']; + slc[FOO.field] = 'b'; + + let _ = &((((FOO)))); +} + From 4dbc015a5cb992b0912a6692e3f55db8139fd125 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Jun 2014 16:25:47 -0700 Subject: [PATCH 2/3] log: Avoid undefined behavior with LOG_LEVEL As I have recently learned from aturon, it is undefined behavior to have any unsynchronized access exected in parallel with an unsynchronized write. This sort of behavior was possibly invoked by liblog during initialization. The log level constant has been altered to become an atomic variable which prevents undefined behavior. --- src/liblog/lib.rs | 54 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs index 554f27b881b72..a878fc0eccb26 100644 --- a/src/liblog/lib.rs +++ b/src/liblog/lib.rs @@ -124,6 +124,7 @@ use std::mem; use std::os; use std::rt; use std::slice; +use std::sync::atomics; use std::sync::{Once, ONCE_INIT}; use directive::LOG_LEVEL_NAMES; @@ -141,7 +142,7 @@ static DEFAULT_LOG_LEVEL: u32 = 1; /// An unsafe constant that is the maximum logging level of any module /// specified. This is the first line of defense to determining whether a /// logging statement should be run. -static mut LOG_LEVEL: u32 = MAX_LOG_LEVEL; +static mut LOG_LEVEL: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; static mut DIRECTIVES: *const Vec = 0 as *const Vec; @@ -242,7 +243,29 @@ pub fn log(level: u32, loc: &'static LogLocation, args: &fmt::Arguments) { /// safely #[doc(hidden)] #[inline(always)] -pub fn log_level() -> u32 { unsafe { LOG_LEVEL } } +#[cfg(not(stage0))] +pub fn log_level() -> u32 { + // We need accessing the log level to be as fast as possible, so we want to + // use as relaxed of an ordering as possible. Once the log level has been + // initialized one, it will never change. The default log level also + // indicates that we *must* log everything (or at least attempt to run + // initialization). + // + // For this reason, we do a relaxed load here. It can either read the + // initial value of LOG_LEVEL in which case the more expensive check will be + // run. It could also read the updated value of LOG_LEVEL in which case it + // reads the correct value. + // + // Also note that the log level stored is the real log level plus 1 so the + // static initialization of 0 indicates that "everything must be logged" + LOG_LEVEL.load(atomics::Relaxed) as u32 - 1 +} + +/// dox +#[doc(hidden)] +#[inline(always)] +#[cfg(stage0)] +pub fn log_level() -> u32 { unsafe { LOG_LEVEL.load(atomics::Relaxed) as u32 - 1 } } /// Replaces the task-local logger with the specified logger, returning the old /// logger. @@ -282,6 +305,26 @@ pub struct LogLocation { /// logging. This is the second layer of defense about determining whether a /// module's log statement should be emitted or not. #[doc(hidden)] +#[cfg(not(stage0))] +pub fn mod_enabled(level: u32, module: &str) -> bool { + static mut INIT: Once = ONCE_INIT; + INIT.doit(init); + + // It's possible for many threads to be in this function, but only one of + // them will peform the global initialization. All of them will need to + // check again to whether they should really be here or not. Hence, despite + // this check being expanded manually in the logging macro, this function + // checks the log level again. + if level > log_level() { return false } + + // This assertion should never get tripped unless we're in an at_exit + // handler after logging has been torn down and a logging attempt was made. + assert!(!DIRECTIVES.is_null()); + + enabled(level, module, unsafe { (*DIRECTIVES).iter() }) +} +/// dox +#[cfg(stage0)] pub fn mod_enabled(level: u32, module: &str) -> bool { static mut INIT: Once = ONCE_INIT; unsafe { INIT.doit(init); } @@ -291,7 +334,7 @@ pub fn mod_enabled(level: u32, module: &str) -> bool { // again to whether they should really be here or not. Hence, despite this // check being expanded manually in the logging macro, this function checks // the log level again. - if level > unsafe { LOG_LEVEL } { return false } + if level > log_level() { return false } // This assertion should never get tripped unless we're in an at_exit // handler after logging has been torn down and a logging attempt was made. @@ -320,6 +363,7 @@ fn enabled(level: u32, /// /// This is not threadsafe at all, so initialization os performed through a /// `Once` primitive (and this function is called from that primitive). +#[allow(unused_unsafe)] // NOTE: remove after a stage0 snap fn init() { let mut directives = match os::getenv("RUST_LOG") { Some(spec) => directive::parse_logging_spec(spec.as_slice()), @@ -340,8 +384,10 @@ fn init() { }; unsafe { - LOG_LEVEL = max_level; + LOG_LEVEL.store(max_level as uint + 1, atomics::SeqCst); + } + unsafe { assert!(DIRECTIVES.is_null()); DIRECTIVES = mem::transmute(box directives); From 7aa67ec3f394dc21cb10d33249897fd90f9e70b7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Jun 2014 16:03:38 -0700 Subject: [PATCH 3/3] Fix fall out of previous changes --- src/libgreen/lib.rs | 1 + src/libgreen/stack.rs | 17 +++++++++++++ src/libnative/io/process.rs | 1 + src/libnative/io/timer_unix.rs | 32 +++++++++++++++++++++++++ src/librustrt/bookkeeping.rs | 16 +++++++++++++ src/librustrt/local_ptr.rs | 44 +++++++++++++++++++++++++--------- src/libstd/io/tempfile.rs | 24 +++++++++++++++++++ src/libstd/io/test.rs | 25 +++++++++++++++++++ src/libstd/io/util.rs | 8 +++---- src/libstd/os.rs | 10 ++++++++ src/libstd/rt/backtrace.rs | 17 +++++++++++++ src/libstd/rt/util.rs | 15 ++++++++++++ src/libsync/atomics.rs | 6 ++--- src/libsync/mutex.rs | 2 +- src/libsync/one.rs | 8 +++---- 15 files changed, 201 insertions(+), 25 deletions(-) diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 6e4e2ac0feee3..ad07a98ed6125 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -395,6 +395,7 @@ impl SchedPool { /// /// This will configure the pool according to the `config` parameter, and /// initially run `main` inside the pool of schedulers. + #[allow(unused_unsafe)] // NOTE: remove after a stage0 snap pub fn new(config: PoolConfig) -> SchedPool { static mut POOL_ID: AtomicUint = INIT_ATOMIC_UINT; diff --git a/src/libgreen/stack.rs b/src/libgreen/stack.rs index af53766617cfe..7ab18b62bfdd4 100644 --- a/src/libgreen/stack.rs +++ b/src/libgreen/stack.rs @@ -157,6 +157,23 @@ impl StackPool { } } +#[cfg(not(stage0))] +fn max_cached_stacks() -> uint { + static mut AMT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; + match AMT.load(atomics::SeqCst) { + 0 => {} + n => return n - 1, + } + let amt = getenv("RUST_MAX_CACHED_STACKS").and_then(|s| from_str(s.as_slice())); + // This default corresponds to 20M of cache per scheduler (at the + // default size). + let amt = amt.unwrap_or(10); + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + AMT.store(amt + 1, atomics::SeqCst); + return amt; +} +#[cfg(stage0)] fn max_cached_stacks() -> uint { static mut AMT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; match unsafe { AMT.load(atomics::SeqCst) } { diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index c89a40d651351..814ce0928779e 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -932,6 +932,7 @@ fn waitpid(pid: pid_t, deadline: u64) -> IoResult { } #[cfg(unix)] +#[allow(unused_unsafe)] // NOTE: remove after a stage0 snap fn waitpid(pid: pid_t, deadline: u64) -> IoResult { use std::cmp; use std::comm; diff --git a/src/libnative/io/timer_unix.rs b/src/libnative/io/timer_unix.rs index 87c320e0457cb..d0a707f1d4221 100644 --- a/src/libnative/io/timer_unix.rs +++ b/src/libnative/io/timer_unix.rs @@ -202,6 +202,24 @@ fn helper(input: libc::c_int, messages: Receiver, _: ()) { } impl Timer { + #[cfg(not(stage0))] + pub fn new() -> IoResult { + HELPER.boot(|| {}, helper); + + static mut ID: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; + let id = ID.fetch_add(1, atomics::Relaxed); + Ok(Timer { + id: id, + inner: Some(box Inner { + cb: None, + interval: 0, + target: 0, + repeat: false, + id: id, + }) + }) + } + #[cfg(stage0)] pub fn new() -> IoResult { // See notes above regarding using int return value // instead of () @@ -233,6 +251,18 @@ impl Timer { } } + #[cfg(not(stage0))] + fn inner(&mut self) -> Box { + match self.inner.take() { + Some(i) => i, + None => { + let (tx, rx) = channel(); + HELPER.send(RemoveTimer(self.id, tx)); + rx.recv() + } + } + } + #[cfg(stage0)] fn inner(&mut self) -> Box { match self.inner.take() { Some(i) => i, @@ -254,6 +284,7 @@ impl rtio::RtioTimer for Timer { Timer::sleep(msecs); } + #[allow(unused_unsafe)] // NOTE: remove after a stage0 snap fn oneshot(&mut self, msecs: u64, cb: Box) { let now = now(); let mut inner = self.inner(); @@ -266,6 +297,7 @@ impl rtio::RtioTimer for Timer { unsafe { HELPER.send(NewTimer(inner)); } } + #[allow(unused_unsafe)] // NOTE: remove after a stage0 snap fn period(&mut self, msecs: u64, cb: Box) { let now = now(); let mut inner = self.inner(); diff --git a/src/librustrt/bookkeeping.rs b/src/librustrt/bookkeeping.rs index dc96aecff8017..3eaeb7567ec9a 100644 --- a/src/librustrt/bookkeeping.rs +++ b/src/librustrt/bookkeeping.rs @@ -34,11 +34,18 @@ impl Drop for Token { /// Increment the number of live tasks, returning a token which will decrement /// the count when dropped. +#[cfg(stage0)] pub fn increment() -> Token { let _ = unsafe { TASK_COUNT.fetch_add(1, atomics::SeqCst) }; Token { _private: () } } +#[cfg(not(stage0))] +pub fn increment() -> Token { + let _ = TASK_COUNT.fetch_add(1, atomics::SeqCst); + Token { _private: () } +} +#[cfg(stage0)] pub fn decrement() { unsafe { if TASK_COUNT.fetch_sub(1, atomics::SeqCst) == 1 { @@ -47,6 +54,15 @@ pub fn decrement() { } } } +#[cfg(not(stage0))] +pub fn decrement() { + if TASK_COUNT.fetch_sub(1, atomics::SeqCst) == 1 { + unsafe { + 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 diff --git a/src/librustrt/local_ptr.rs b/src/librustrt/local_ptr.rs index c94e5c6187b3a..9b16d53c0e461 100644 --- a/src/librustrt/local_ptr.rs +++ b/src/librustrt/local_ptr.rs @@ -207,11 +207,18 @@ pub mod compiled { /// Check whether there is a thread-local pointer installed. #[inline(never)] // see comments above + #[cfg(stage0)] pub fn exists() -> bool { unsafe { RT_TLS_PTR.is_not_null() } } + /// Check whether there is a thread-local pointer installed. + #[inline(never)] // see comments above + #[cfg(not(stage0))] + pub fn exists() -> bool { + RT_TLS_PTR.is_not_null() + } #[inline(never)] // see comments above pub unsafe fn unsafe_borrow() -> *mut T { @@ -373,20 +380,17 @@ pub mod native { } #[inline] - #[cfg(not(test))] + #[cfg(not(test), stage0)] #[allow(visible_private_types)] 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. + // NB: This could lead to undefined behavior because it is an + // unsynchronized read of a global, but it's generally undefined to + // boot the runtime as you start using it. + // + // This global is written immediately on program startup (before all + // usage of it), so all proper usage of the runtime will not invoke + // undefined behvaior here. if RT_TLS_KEY != -1 { return Some(RT_TLS_KEY); } else { @@ -395,6 +399,24 @@ pub mod native { } } + #[inline] + #[cfg(not(test), not(stage0))] + #[allow(visible_private_types)] + pub fn maybe_tls_key() -> Option { + // NB: This could lead to undefined behavior because it is an + // unsynchronized read of a global, but it's generally undefined to boot + // the runtime as you start using it. + // + // This global is written immediately on program startup (before all + // usage of it), so all proper usage of the runtime will not invoke + // undefined behvaior here. + if RT_TLS_KEY != -1 { + return Some(RT_TLS_KEY); + } else { + return None; + } + } + #[inline] #[cfg(test)] pub fn maybe_tls_key() -> Option { use realrustrt; diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs index f580dfd80f0ce..d0cfe6719233d 100644 --- a/src/libstd/io/tempfile.rs +++ b/src/libstd/io/tempfile.rs @@ -37,6 +37,30 @@ impl TempDir { /// deleted once the returned wrapper is destroyed. /// /// If no directory can be created, None is returned. + #[cfg(not(stage0))] + pub fn new_in(tmpdir: &Path, suffix: &str) -> Option { + if !tmpdir.is_absolute() { + return TempDir::new_in(&os::make_absolute(tmpdir), suffix); + } + + static mut CNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; + + for _ in range(0u, 1000) { + let filename = + format!("rs-{}-{}-{}", + unsafe { libc::getpid() }, + CNT.fetch_add(1, atomics::SeqCst), + suffix); + let p = tmpdir.join(filename); + match fs::mkdir(&p, io::UserRWX) { + Err(..) => {} + Ok(()) => return Some(TempDir { path: Some(p), disarmed: false }) + } + } + None + } + /// dox + #[cfg(stage0)] pub fn new_in(tmpdir: &Path, suffix: &str) -> Option { if !tmpdir.is_absolute() { return TempDir::new_in(&os::make_absolute(tmpdir), suffix); diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 26e854d9d999f..90488eee4341e 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -54,6 +54,13 @@ macro_rules! iotest ( ) /// Get a port number, starting at 9600, for use in tests +#[cfg(not(stage0))] +pub fn next_test_port() -> u16 { + static mut next_offset: AtomicUint = INIT_ATOMIC_UINT; + base_port() + next_offset.fetch_add(1, Relaxed) as u16 +} +/// dox +#[cfg(stage0)] pub fn next_test_port() -> u16 { static mut next_offset: AtomicUint = INIT_ATOMIC_UINT; unsafe { @@ -62,6 +69,24 @@ pub fn next_test_port() -> u16 { } /// Get a temporary path which could be the location of a unix socket +#[cfg(not(stage0))] +pub fn next_test_unix() -> Path { + static mut COUNT: AtomicUint = INIT_ATOMIC_UINT; + // base port and pid are an attempt to be unique between multiple + // test-runners of different configurations running on one + // buildbot, the count is to be unique within this executable. + let string = format!("rust-test-unix-path-{}-{}-{}", + base_port(), + unsafe {libc::getpid()}, + COUNT.fetch_add(1, Relaxed)); + if cfg!(unix) { + os::tmpdir().join(string) + } else { + Path::new(format!("{}{}", r"\\.\pipe\", string)) + } +} +/// dox +#[cfg(stage0)] pub fn next_test_unix() -> Path { static mut COUNT: AtomicUint = INIT_ATOMIC_UINT; // base port and pid are an attempt to be unique between multiple diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index e928323030c4f..7c30bb295257c 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -341,11 +341,11 @@ mod test { let mut multi = MultiWriter::new(vec!(box TestWriter as Box, box TestWriter as Box)); multi.write([1, 2, 3]).unwrap(); - assert_eq!(2, unsafe { writes }); - assert_eq!(0, unsafe { flushes }); + assert_eq!(2, writes); + assert_eq!(0, flushes); multi.flush().unwrap(); - assert_eq!(2, unsafe { writes }); - assert_eq!(2, unsafe { flushes }); + assert_eq!(2, writes); + assert_eq!(2, flushes); } #[test] diff --git a/src/libstd/os.rs b/src/libstd/os.rs index 7fff510a60a4b..2906a3b9af44d 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -1082,12 +1082,22 @@ static mut EXIT_STATUS: AtomicInt = INIT_ATOMIC_INT; * * Note that this is not synchronized against modifications of other threads. */ +#[cfg(not(stage0))] +pub fn set_exit_status(code: int) { + EXIT_STATUS.store(code, SeqCst) +} +#[cfg(stage0)] pub fn set_exit_status(code: int) { unsafe { EXIT_STATUS.store(code, SeqCst) } } /// Fetches the process's current exit code. This defaults to 0 and can change /// by calling `set_exit_status`. +#[cfg(not(stage0))] +pub fn get_exit_status() -> int { + EXIT_STATUS.load(SeqCst) +} +#[cfg(stage0)] pub fn get_exit_status() -> int { unsafe { EXIT_STATUS.load(SeqCst) } } diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 80493ebb4a936..15e46ac246f91 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -27,6 +27,23 @@ pub use self::imp::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. +#[cfg(not(stage0))] +pub fn log_enabled() -> bool { + static mut ENABLED: atomics::AtomicInt = atomics::INIT_ATOMIC_INT; + match ENABLED.load(atomics::SeqCst) { + 1 => return false, + 2 => return true, + _ => {} + } + + let val = match os::getenv("RUST_BACKTRACE") { + Some(..) => 2, + None => 1, + }; + ENABLED.store(val, atomics::SeqCst); + val == 2 +} +#[cfg(stage0)] pub fn log_enabled() -> bool { static mut ENABLED: atomics::AtomicInt = atomics::INIT_ATOMIC_INT; unsafe { diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index fa30ddbcc48b5..0fc6e94c252ff 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -40,6 +40,21 @@ pub fn limit_thread_creation_due_to_osx_and_valgrind() -> bool { (cfg!(target_os="macos")) && running_on_valgrind() } +#[cfg(not(stage0))] +pub fn min_stack() -> uint { + static mut MIN: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; + match MIN.load(atomics::SeqCst) { + 0 => {} + n => return n - 1, + } + let amt = os::getenv("RUST_MIN_STACK").and_then(|s| from_str(s.as_slice())); + let amt = amt.unwrap_or(2 * 1024 * 1024); + // 0 is our sentinel value, so ensure that we'll never see 0 after + // initialization has run + MIN.store(amt + 1, atomics::SeqCst); + return amt; +} +#[cfg(stage0)] pub fn min_stack() -> uint { static mut MIN: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; match unsafe { MIN.load(atomics::SeqCst) } { diff --git a/src/libsync/atomics.rs b/src/libsync/atomics.rs index 0be124ad58408..255669390bad2 100644 --- a/src/libsync/atomics.rs +++ b/src/libsync/atomics.rs @@ -95,10 +95,8 @@ //! //! static mut GLOBAL_TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; //! -//! unsafe { -//! let old_task_count = GLOBAL_TASK_COUNT.fetch_add(1, SeqCst); -//! println!("live tasks: {}", old_task_count + 1); -//! } +//! let old_task_count = GLOBAL_TASK_COUNT.fetch_add(1, SeqCst); +//! println!("live tasks: {}", old_task_count + 1); //! ``` use core::prelude::*; diff --git a/src/libsync/mutex.rs b/src/libsync/mutex.rs index 1aa84e8f8d149..fccd614acc711 100644 --- a/src/libsync/mutex.rs +++ b/src/libsync/mutex.rs @@ -129,7 +129,7 @@ enum Flavor { /// /// static mut LOCK: StaticMutex = MUTEX_INIT; /// -/// unsafe { +/// { /// let _g = LOCK.lock(); /// // do some productive work /// } diff --git a/src/libsync/one.rs b/src/libsync/one.rs index 6fad2c8aa404d..d4dc5b64c99bf 100644 --- a/src/libsync/one.rs +++ b/src/libsync/one.rs @@ -32,11 +32,9 @@ use mutex::{StaticMutex, MUTEX_INIT}; /// /// static mut START: Once = ONCE_INIT; /// -/// unsafe { -/// START.doit(|| { -/// // run initialization here -/// }); -/// } +/// START.doit(|| { +/// // run initialization here +/// }); /// ``` pub struct Once { mutex: StaticMutex,