From a55c57284d8341ee5b22c5372e77ac0af9479dc5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Dec 2013 17:27:37 -0800 Subject: std: Introduce std::sync For now, this moves the following modules to std::sync * UnsafeArc (also removed unwrap method) * mpsc_queue * spsc_queue * atomics * mpmc_bounded_queue * deque We may want to remove some of the queues, but for now this moves things out of std::rt into std::sync --- src/libstd/sync/arc.rs | 153 ++++++++ src/libstd/sync/atomics.rs | 603 +++++++++++++++++++++++++++++++ src/libstd/sync/deque.rs | 661 ++++++++++++++++++++++++++++++++++ src/libstd/sync/mod.rs | 23 ++ src/libstd/sync/mpmc_bounded_queue.rs | 211 +++++++++++ src/libstd/sync/mpsc_queue.rs | 245 +++++++++++++ src/libstd/sync/spsc_queue.rs | 334 +++++++++++++++++ 7 files changed, 2230 insertions(+) create mode 100644 src/libstd/sync/arc.rs create mode 100644 src/libstd/sync/atomics.rs create mode 100644 src/libstd/sync/deque.rs create mode 100644 src/libstd/sync/mod.rs create mode 100644 src/libstd/sync/mpmc_bounded_queue.rs create mode 100644 src/libstd/sync/mpsc_queue.rs create mode 100644 src/libstd/sync/spsc_queue.rs (limited to 'src/libstd/sync') diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs new file mode 100644 index 00000000000..7632ec6cf29 --- /dev/null +++ b/src/libstd/sync/arc.rs @@ -0,0 +1,153 @@ +// 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. + +//! Atomically reference counted data +//! +//! This modules contains the implementation of an atomically reference counted +//! pointer for the purpose of sharing data between tasks. This is obviously a +//! very unsafe primitive to use, but it has its use cases when implementing +//! concurrent data structures and similar tasks. +//! +//! Great care must be taken to ensure that data races do not arise through the +//! usage of `UnsafeArc`, and this often requires some form of external +//! synchronization. The only guarantee provided to you by this class is that +//! the underlying data will remain valid (not free'd) so long as the reference +//! count is greater than one. + +use cast; +use clone::Clone; +use kinds::Send; +use ops::Drop; +use ptr::RawPtr; +use sync::atomics::{AtomicUint, SeqCst, Relaxed, Acquire}; +use vec; + +/// An atomically reference counted pointer. +/// +/// Enforces no shared-memory safety. +#[unsafe_no_drop_flag] +pub struct UnsafeArc { + priv data: *mut ArcData, +} + +struct ArcData { + count: AtomicUint, + data: T, +} + +unsafe fn new_inner(data: T, refcount: uint) -> *mut ArcData { + let data = ~ArcData { count: AtomicUint::new(refcount), data: data }; + cast::transmute(data) +} + +impl UnsafeArc { + /// Creates a new `UnsafeArc` which wraps the given data. + pub fn new(data: T) -> UnsafeArc { + unsafe { UnsafeArc { data: new_inner(data, 1) } } + } + + /// As new(), but returns an extra pre-cloned handle. + pub fn new2(data: T) -> (UnsafeArc, UnsafeArc) { + unsafe { + let ptr = new_inner(data, 2); + (UnsafeArc { data: ptr }, UnsafeArc { data: ptr }) + } + } + + /// As new(), but returns a vector of as many pre-cloned handles as + /// requested. + pub fn newN(data: T, num_handles: uint) -> ~[UnsafeArc] { + unsafe { + if num_handles == 0 { + ~[] // need to free data here + } else { + let ptr = new_inner(data, num_handles); + vec::from_fn(num_handles, |_| UnsafeArc { data: ptr }) + } + } + } + + /// Gets a pointer to the inner shared data. Note that care must be taken to + /// ensure that the outer `UnsafeArc` does not fall out of scope while this + /// pointer is in use, otherwise it could possibly contain a use-after-free. + #[inline] + pub fn get(&self) -> *mut T { + unsafe { + assert!((*self.data).count.load(Relaxed) > 0); + return &mut (*self.data).data as *mut T; + } + } + + /// Gets an immutable pointer to the inner shared data. This has the same + /// caveats as the `get` method. + #[inline] + pub fn get_immut(&self) -> *T { + unsafe { + assert!((*self.data).count.load(Relaxed) > 0); + return &(*self.data).data as *T; + } + } +} + +impl Clone for UnsafeArc { + fn clone(&self) -> UnsafeArc { + unsafe { + // This barrier might be unnecessary, but I'm not sure... + let old_count = (*self.data).count.fetch_add(1, Acquire); + assert!(old_count >= 1); + return UnsafeArc { data: self.data }; + } + } +} + +#[unsafe_destructor] +impl Drop for UnsafeArc{ + fn drop(&mut self) { + unsafe { + // Happens when destructing an unwrapper's handle and from + // `#[unsafe_no_drop_flag]` + if self.data.is_null() { + return + } + // Must be acquire+release, not just release, to make sure this + // doesn't get reordered to after the unwrapper pointer load. + let old_count = (*self.data).count.fetch_sub(1, SeqCst); + assert!(old_count >= 1); + if old_count == 1 { + let _: ~ArcData = cast::transmute(self.data); + } + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::UnsafeArc; + use task; + use mem::size_of; + + #[test] + fn test_size() { + assert_eq!(size_of::>(), size_of::<*[int, ..10]>()); + } + + #[test] + fn arclike_newN() { + // Tests that the many-refcounts-at-once constructors don't leak. + let _ = UnsafeArc::new2(~~"hello"); + let x = UnsafeArc::newN(~~"hello", 0); + assert_eq!(x.len(), 0) + let x = UnsafeArc::newN(~~"hello", 1); + assert_eq!(x.len(), 1) + let x = UnsafeArc::newN(~~"hello", 10); + assert_eq!(x.len(), 10) + } +} diff --git a/src/libstd/sync/atomics.rs b/src/libstd/sync/atomics.rs new file mode 100644 index 00000000000..bc9d99c0f37 --- /dev/null +++ b/src/libstd/sync/atomics.rs @@ -0,0 +1,603 @@ +// Copyright 2012-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. + +/*! + * Atomic types + * + * Basic atomic types supporting atomic operations. Each method takes an + * `Ordering` which represents the strength of the memory barrier for that + * operation. These orderings are the same as C++11 atomic orderings + * [http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync] + * + * All atomic types are a single word in size. + */ + +#[allow(missing_doc)]; + +use unstable::intrinsics; +use cast; +use option::{Option,Some,None}; +use libc::c_void; +use ops::Drop; +use util::NonCopyable; + +/** + * A simple atomic flag, that can be set and cleared. The most basic atomic type. + */ +pub struct AtomicFlag { + priv v: int, + priv nocopy: NonCopyable +} + +/** + * An atomic boolean type. + */ +pub struct AtomicBool { + priv v: uint, + priv nocopy: NonCopyable +} + +/** + * A signed atomic integer type, supporting basic atomic arithmetic operations + */ +pub struct AtomicInt { + priv v: int, + priv nocopy: NonCopyable +} + +/** + * An unsigned atomic integer type, supporting basic atomic arithmetic operations + */ +pub struct AtomicUint { + priv v: uint, + priv nocopy: NonCopyable +} + +/** + * An unsafe atomic pointer. Only supports basic atomic operations + */ +pub struct AtomicPtr { + priv p: *mut T, + priv nocopy: NonCopyable +} + +/** + * An owned atomic pointer. Ensures that only a single reference to the data is held at any time. + */ +#[unsafe_no_drop_flag] +pub struct AtomicOption { + priv p: *mut c_void +} + +pub enum Ordering { + Relaxed, + Release, + Acquire, + AcqRel, + SeqCst +} + +pub static INIT_ATOMIC_FLAG : AtomicFlag = AtomicFlag { v: 0, nocopy: NonCopyable }; +pub static INIT_ATOMIC_BOOL : AtomicBool = AtomicBool { v: 0, nocopy: NonCopyable }; +pub static INIT_ATOMIC_INT : AtomicInt = AtomicInt { v: 0, nocopy: NonCopyable }; +pub static INIT_ATOMIC_UINT : AtomicUint = AtomicUint { v: 0, nocopy: NonCopyable }; + +impl AtomicFlag { + + pub fn new() -> AtomicFlag { + AtomicFlag { v: 0, nocopy: NonCopyable } + } + + /** + * Clears the atomic flag + */ + #[inline] + pub fn clear(&mut self, order: Ordering) { + unsafe {atomic_store(&mut self.v, 0, order)} + } + + /** + * Sets the flag if it was previously unset, returns the previous value of the + * flag. + */ + #[inline] + pub fn test_and_set(&mut self, order: Ordering) -> bool { + unsafe { atomic_compare_and_swap(&mut self.v, 0, 1, order) > 0 } + } +} + +impl AtomicBool { + pub fn new(v: bool) -> AtomicBool { + AtomicBool { v: if v { 1 } else { 0 }, nocopy: NonCopyable } + } + + #[inline] + pub fn load(&self, order: Ordering) -> bool { + unsafe { atomic_load(&self.v, order) > 0 } + } + + #[inline] + pub fn store(&mut self, val: bool, order: Ordering) { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline] + pub fn swap(&mut self, val: bool, order: Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_swap(&mut self.v, val, order) > 0 } + } + + #[inline] + pub fn compare_and_swap(&mut self, old: bool, new: bool, order: Ordering) -> bool { + let old = if old { 1 } else { 0 }; + let new = if new { 1 } else { 0 }; + + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) > 0 } + } + + /// Returns the old value + #[inline] + pub fn fetch_and(&mut self, val: bool, order: Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_and(&mut self.v, val, order) > 0 } + } + + /// Returns the old value + #[inline] + pub fn fetch_nand(&mut self, val: bool, order: Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_nand(&mut self.v, val, order) > 0 } + } + + /// Returns the old value + #[inline] + pub fn fetch_or(&mut self, val: bool, order: Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_or(&mut self.v, val, order) > 0 } + } + + /// Returns the old value + #[inline] + pub fn fetch_xor(&mut self, val: bool, order: Ordering) -> bool { + let val = if val { 1 } else { 0 }; + + unsafe { atomic_xor(&mut self.v, val, order) > 0 } + } +} + +impl AtomicInt { + pub fn new(v: int) -> AtomicInt { + AtomicInt { v:v, nocopy: NonCopyable } + } + + #[inline] + pub fn load(&self, order: Ordering) -> int { + unsafe { atomic_load(&self.v, order) } + } + + #[inline] + pub fn store(&mut self, val: int, order: Ordering) { + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline] + pub fn swap(&mut self, val: int, order: Ordering) -> int { + unsafe { atomic_swap(&mut self.v, val, order) } + } + + #[inline] + pub fn compare_and_swap(&mut self, old: int, new: int, order: Ordering) -> int { + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) } + } + + /// Returns the old value (like __sync_fetch_and_add). + #[inline] + pub fn fetch_add(&mut self, val: int, order: Ordering) -> int { + unsafe { atomic_add(&mut self.v, val, order) } + } + + /// Returns the old value (like __sync_fetch_and_sub). + #[inline] + pub fn fetch_sub(&mut self, val: int, order: Ordering) -> int { + unsafe { atomic_sub(&mut self.v, val, order) } + } +} + +impl AtomicUint { + pub fn new(v: uint) -> AtomicUint { + AtomicUint { v:v, nocopy: NonCopyable } + } + + #[inline] + pub fn load(&self, order: Ordering) -> uint { + unsafe { atomic_load(&self.v, order) } + } + + #[inline] + pub fn store(&mut self, val: uint, order: Ordering) { + unsafe { atomic_store(&mut self.v, val, order); } + } + + #[inline] + pub fn swap(&mut self, val: uint, order: Ordering) -> uint { + unsafe { atomic_swap(&mut self.v, val, order) } + } + + #[inline] + pub fn compare_and_swap(&mut self, old: uint, new: uint, order: Ordering) -> uint { + unsafe { atomic_compare_and_swap(&mut self.v, old, new, order) } + } + + /// Returns the old value (like __sync_fetch_and_add). + #[inline] + pub fn fetch_add(&mut self, val: uint, order: Ordering) -> uint { + unsafe { atomic_add(&mut self.v, val, order) } + } + + /// Returns the old value (like __sync_fetch_and_sub).. + #[inline] + pub fn fetch_sub(&mut self, val: uint, order: Ordering) -> uint { + unsafe { atomic_sub(&mut self.v, val, order) } + } +} + +impl AtomicPtr { + pub fn new(p: *mut T) -> AtomicPtr { + AtomicPtr { p:p, nocopy: NonCopyable } + } + + #[inline] + pub fn load(&self, order: Ordering) -> *mut T { + unsafe { atomic_load(&self.p, order) } + } + + #[inline] + pub fn store(&mut self, ptr: *mut T, order: Ordering) { + unsafe { atomic_store(&mut self.p, ptr, order); } + } + + #[inline] + pub fn swap(&mut self, ptr: *mut T, order: Ordering) -> *mut T { + unsafe { atomic_swap(&mut self.p, ptr, order) } + } + + #[inline] + pub fn compare_and_swap(&mut self, old: *mut T, new: *mut T, order: Ordering) -> *mut T { + unsafe { atomic_compare_and_swap(&mut self.p, old, new, order) } + } +} + +impl AtomicOption { + pub fn new(p: ~T) -> AtomicOption { + unsafe { + AtomicOption { + p: cast::transmute(p) + } + } + } + + pub fn empty() -> AtomicOption { + unsafe { + AtomicOption { + p: cast::transmute(0) + } + } + } + + #[inline] + pub fn swap(&mut self, val: ~T, order: Ordering) -> Option<~T> { + unsafe { + let val = cast::transmute(val); + + let p = atomic_swap(&mut self.p, val, order); + let pv : &uint = cast::transmute(&p); + + if *pv == 0 { + None + } else { + Some(cast::transmute(p)) + } + } + } + + #[inline] + pub fn take(&mut self, order: Ordering) -> Option<~T> { + unsafe { + self.swap(cast::transmute(0), order) + } + } + + /// A compare-and-swap. Succeeds if the option is 'None' and returns 'None' + /// if so. If the option was already 'Some', returns 'Some' of the rejected + /// value. + #[inline] + pub fn fill(&mut self, val: ~T, order: Ordering) -> Option<~T> { + unsafe { + let val = cast::transmute(val); + let expected = cast::transmute(0); + let oldval = atomic_compare_and_swap(&mut self.p, expected, val, order); + if oldval == expected { + None + } else { + Some(cast::transmute(val)) + } + } + } + + /// Be careful: The caller must have some external method of ensuring the + /// result does not get invalidated by another task after this returns. + #[inline] + pub fn is_empty(&mut self, order: Ordering) -> bool { + unsafe { atomic_load(&self.p, order) == cast::transmute(0) } + } +} + +#[unsafe_destructor] +impl Drop for AtomicOption { + fn drop(&mut self) { + let _ = self.take(SeqCst); + } +} + +#[inline] +pub unsafe fn atomic_store(dst: &mut T, val: T, order:Ordering) { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + match order { + Release => intrinsics::atomic_store_rel(dst, val), + Relaxed => intrinsics::atomic_store_relaxed(dst, val), + _ => intrinsics::atomic_store(dst, val) + } +} + +#[inline] +pub unsafe fn atomic_load(dst: &T, order:Ordering) -> T { + let dst = cast::transmute(dst); + + cast::transmute(match order { + Acquire => intrinsics::atomic_load_acq(dst), + Relaxed => intrinsics::atomic_load_relaxed(dst), + _ => intrinsics::atomic_load(dst) + }) +} + +#[inline] +pub unsafe fn atomic_swap(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xchg_acq(dst, val), + Release => intrinsics::atomic_xchg_rel(dst, val), + AcqRel => intrinsics::atomic_xchg_acqrel(dst, val), + Relaxed => intrinsics::atomic_xchg_relaxed(dst, val), + _ => intrinsics::atomic_xchg(dst, val) + }) +} + +/// Returns the old value (like __sync_fetch_and_add). +#[inline] +pub unsafe fn atomic_add(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xadd_acq(dst, val), + Release => intrinsics::atomic_xadd_rel(dst, val), + AcqRel => intrinsics::atomic_xadd_acqrel(dst, val), + Relaxed => intrinsics::atomic_xadd_relaxed(dst, val), + _ => intrinsics::atomic_xadd(dst, val) + }) +} + +/// Returns the old value (like __sync_fetch_and_sub). +#[inline] +pub unsafe fn atomic_sub(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xsub_acq(dst, val), + Release => intrinsics::atomic_xsub_rel(dst, val), + AcqRel => intrinsics::atomic_xsub_acqrel(dst, val), + Relaxed => intrinsics::atomic_xsub_relaxed(dst, val), + _ => intrinsics::atomic_xsub(dst, val) + }) +} + +#[inline] +pub unsafe fn atomic_compare_and_swap(dst:&mut T, old:T, new:T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let old = cast::transmute(old); + let new = cast::transmute(new); + + cast::transmute(match order { + Acquire => intrinsics::atomic_cxchg_acq(dst, old, new), + Release => intrinsics::atomic_cxchg_rel(dst, old, new), + AcqRel => intrinsics::atomic_cxchg_acqrel(dst, old, new), + Relaxed => intrinsics::atomic_cxchg_relaxed(dst, old, new), + _ => intrinsics::atomic_cxchg(dst, old, new), + }) +} + +#[inline] +pub unsafe fn atomic_and(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_and_acq(dst, val), + Release => intrinsics::atomic_and_rel(dst, val), + AcqRel => intrinsics::atomic_and_acqrel(dst, val), + Relaxed => intrinsics::atomic_and_relaxed(dst, val), + _ => intrinsics::atomic_and(dst, val) + }) +} + + +#[inline] +pub unsafe fn atomic_nand(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_nand_acq(dst, val), + Release => intrinsics::atomic_nand_rel(dst, val), + AcqRel => intrinsics::atomic_nand_acqrel(dst, val), + Relaxed => intrinsics::atomic_nand_relaxed(dst, val), + _ => intrinsics::atomic_nand(dst, val) + }) +} + + +#[inline] +pub unsafe fn atomic_or(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_or_acq(dst, val), + Release => intrinsics::atomic_or_rel(dst, val), + AcqRel => intrinsics::atomic_or_acqrel(dst, val), + Relaxed => intrinsics::atomic_or_relaxed(dst, val), + _ => intrinsics::atomic_or(dst, val) + }) +} + + +#[inline] +pub unsafe fn atomic_xor(dst: &mut T, val: T, order: Ordering) -> T { + let dst = cast::transmute(dst); + let val = cast::transmute(val); + + cast::transmute(match order { + Acquire => intrinsics::atomic_xor_acq(dst, val), + Release => intrinsics::atomic_xor_rel(dst, val), + AcqRel => intrinsics::atomic_xor_acqrel(dst, val), + Relaxed => intrinsics::atomic_xor_relaxed(dst, val), + _ => intrinsics::atomic_xor(dst, val) + }) +} + + +/** + * An atomic fence. + * + * A fence 'A' which has `Release` ordering semantics, synchronizes with a + * fence 'B' with (at least) `Acquire` semantics, if and only if there exists + * atomic operations X and Y, both operating on some atomic object 'M' such + * that A is sequenced before X, Y is synchronized before B and Y observers + * the change to M. This provides a happens-before dependence between A and B. + * + * Atomic operations with `Release` or `Acquire` semantics can also synchronize + * with a fence. + * + * A fence with has `SeqCst` ordering, in addition to having both `Acquire` and + * `Release` semantics, participates in the global program order of the other + * `SeqCst` operations and/or fences. + * + * Accepts `Acquire`, `Release`, `AcqRel` and `SeqCst` orderings. + */ +#[inline] +pub fn fence(order: Ordering) { + unsafe { + match order { + Acquire => intrinsics::atomic_fence_acq(), + Release => intrinsics::atomic_fence_rel(), + AcqRel => intrinsics::atomic_fence_rel(), + _ => intrinsics::atomic_fence(), + } + } +} + +#[cfg(test)] +mod test { + use option::*; + use super::*; + + #[test] + fn flag() { + let mut flg = AtomicFlag::new(); + assert!(!flg.test_and_set(SeqCst)); + assert!(flg.test_and_set(SeqCst)); + + flg.clear(SeqCst); + assert!(!flg.test_and_set(SeqCst)); + } + + #[test] + fn option_empty() { + let mut option: AtomicOption<()> = AtomicOption::empty(); + assert!(option.is_empty(SeqCst)); + } + + #[test] + fn option_swap() { + let mut p = AtomicOption::new(~1); + let a = ~2; + + let b = p.swap(a, SeqCst); + + assert_eq!(b, Some(~1)); + assert_eq!(p.take(SeqCst), Some(~2)); + } + + #[test] + fn option_take() { + let mut p = AtomicOption::new(~1); + + assert_eq!(p.take(SeqCst), Some(~1)); + assert_eq!(p.take(SeqCst), None); + + let p2 = ~2; + p.swap(p2, SeqCst); + + assert_eq!(p.take(SeqCst), Some(~2)); + } + + #[test] + fn option_fill() { + let mut p = AtomicOption::new(~1); + assert!(p.fill(~2, SeqCst).is_some()); // should fail; shouldn't leak! + assert_eq!(p.take(SeqCst), Some(~1)); + + assert!(p.fill(~2, SeqCst).is_none()); // shouldn't fail + assert_eq!(p.take(SeqCst), Some(~2)); + } + + #[test] + fn bool_and() { + let mut a = AtomicBool::new(true); + assert_eq!(a.fetch_and(false, SeqCst),true); + assert_eq!(a.load(SeqCst),false); + } + + static mut S_FLAG : AtomicFlag = INIT_ATOMIC_FLAG; + static mut S_BOOL : AtomicBool = INIT_ATOMIC_BOOL; + static mut S_INT : AtomicInt = INIT_ATOMIC_INT; + static mut S_UINT : AtomicUint = INIT_ATOMIC_UINT; + + #[test] + fn static_init() { + unsafe { + assert!(!S_FLAG.test_and_set(SeqCst)); + assert!(!S_BOOL.load(SeqCst)); + assert!(S_INT.load(SeqCst) == 0); + assert!(S_UINT.load(SeqCst) == 0); + } + } +} diff --git a/src/libstd/sync/deque.rs b/src/libstd/sync/deque.rs new file mode 100644 index 00000000000..4d0efcd6ee1 --- /dev/null +++ b/src/libstd/sync/deque.rs @@ -0,0 +1,661 @@ +// 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. + +//! A (mostly) lock-free concurrent work-stealing deque +//! +//! This module contains an implementation of the Chase-Lev work stealing deque +//! described in "Dynamic Circular Work-Stealing Deque". The implementation is +//! heavily based on the pseudocode found in the paper. +//! +//! This implementation does not want to have the restriction of a garbage +//! collector for reclamation of buffers, and instead it uses a shared pool of +//! buffers. This shared pool is required for correctness in this +//! implementation. +//! +//! The only lock-synchronized portions of this deque are the buffer allocation +//! and deallocation portions. Otherwise all operations are lock-free. +//! +//! # Example +//! +//! use std::rt::deque::BufferPool; +//! +//! let mut pool = BufferPool::new(); +//! let (mut worker, mut stealer) = pool.deque(); +//! +//! // Only the worker may push/pop +//! worker.push(1); +//! worker.pop(); +//! +//! // Stealers take data from the other end of the deque +//! worker.push(1); +//! stealer.steal(); +//! +//! // Stealers can be cloned to have many stealers stealing in parallel +//! worker.push(1); +//! let mut stealer2 = stealer.clone(); +//! stealer2.steal(); + +// NB: the "buffer pool" strategy is not done for speed, but rather for +// correctness. For more info, see the comment on `swap_buffer` + +// XXX: all atomic operations in this module use a SeqCst ordering. That is +// probably overkill + +use cast; +use clone::Clone; +use iter::{range, Iterator}; +use kinds::Send; +use libc; +use mem; +use ops::Drop; +use option::{Option, Some, None}; +use ptr; +use ptr::RawPtr; +use sync::arc::UnsafeArc; +use sync::atomics::{AtomicInt, AtomicPtr, SeqCst}; +use unstable::sync::Exclusive; +use vec::{OwnedVector, ImmutableVector}; + +// Once the queue is less than 1/K full, then it will be downsized. Note that +// the deque requires that this number be less than 2. +static K: int = 4; + +// Minimum number of bits that a buffer size should be. No buffer will resize to +// under this value, and all deques will initially contain a buffer of this +// size. +// +// The size in question is 1 << MIN_BITS +static MIN_BITS: int = 7; + +struct Deque { + bottom: AtomicInt, + top: AtomicInt, + array: AtomicPtr>, + pool: BufferPool, +} + +/// Worker half of the work-stealing deque. This worker has exclusive access to +/// one side of the deque, and uses `push` and `pop` method to manipulate it. +/// +/// There may only be one worker per deque. +pub struct Worker { + priv deque: UnsafeArc>, +} + +/// The stealing half of the work-stealing deque. Stealers have access to the +/// opposite end of the deque from the worker, and they only have access to the +/// `steal` method. +pub struct Stealer { + priv deque: UnsafeArc>, +} + +/// When stealing some data, this is an enumeration of the possible outcomes. +#[deriving(Eq)] +pub enum Stolen { + /// The deque was empty at the time of stealing + Empty, + /// The stealer lost the race for stealing data, and a retry may return more + /// data. + Abort, + /// The stealer has successfully stolen some data. + Data(T), +} + +/// The allocation pool for buffers used by work-stealing deques. Right now this +/// structure is used for reclamation of memory after it is no longer in use by +/// deques. +/// +/// This data structure is protected by a mutex, but it is rarely used. Deques +/// will only use this structure when allocating a new buffer or deallocating a +/// previous one. +pub struct BufferPool { + priv pool: Exclusive<~[~Buffer]>, +} + +/// An internal buffer used by the chase-lev deque. This structure is actually +/// implemented as a circular buffer, and is used as the intermediate storage of +/// the data in the deque. +/// +/// This type is implemented with *T instead of ~[T] for two reasons: +/// +/// 1. There is nothing safe about using this buffer. This easily allows the +/// same value to be read twice in to rust, and there is nothing to +/// prevent this. The usage by the deque must ensure that one of the +/// values is forgotten. Furthermore, we only ever want to manually run +/// destructors for values in this buffer (on drop) because the bounds +/// are defined by the deque it's owned by. +/// +/// 2. We can certainly avoid bounds checks using *T instead of ~[T], although +/// LLVM is probably pretty good at doing this already. +struct Buffer { + storage: *T, + log_size: int, +} + +impl BufferPool { + /// Allocates a new buffer pool which in turn can be used to allocate new + /// deques. + pub fn new() -> BufferPool { + BufferPool { pool: Exclusive::new(~[]) } + } + + /// Allocates a new work-stealing deque which will send/receiving memory to + /// and from this buffer pool. + pub fn deque(&mut self) -> (Worker, Stealer) { + let (a, b) = UnsafeArc::new2(Deque::new(self.clone())); + (Worker { deque: a }, Stealer { deque: b }) + } + + fn alloc(&mut self, bits: int) -> ~Buffer { + unsafe { + self.pool.with(|pool| { + match pool.iter().position(|x| x.size() >= (1 << bits)) { + Some(i) => pool.remove(i), + None => ~Buffer::new(bits) + } + }) + } + } + + fn free(&mut self, buf: ~Buffer) { + unsafe { + let mut buf = Some(buf); + self.pool.with(|pool| { + let buf = buf.take_unwrap(); + match pool.iter().position(|v| v.size() > buf.size()) { + Some(i) => pool.insert(i, buf), + None => pool.push(buf), + } + }) + } + } +} + +impl Clone for BufferPool { + fn clone(&self) -> BufferPool { BufferPool { pool: self.pool.clone() } } +} + +impl Worker { + /// Pushes data onto the front of this work queue. + pub fn push(&mut self, t: T) { + unsafe { (*self.deque.get()).push(t) } + } + /// Pops data off the front of the work queue, returning `None` on an empty + /// queue. + pub fn pop(&mut self) -> Option { + unsafe { (*self.deque.get()).pop() } + } + + /// Gets access to the buffer pool that this worker is attached to. This can + /// be used to create more deques which share the same buffer pool as this + /// deque. + pub fn pool<'a>(&'a mut self) -> &'a mut BufferPool { + unsafe { &mut (*self.deque.get()).pool } + } +} + +impl Stealer { + /// Steals work off the end of the queue (opposite of the worker's end) + pub fn steal(&mut self) -> Stolen { + unsafe { (*self.deque.get()).steal() } + } + + /// Gets access to the buffer pool that this stealer is attached to. This + /// can be used to create more deques which share the same buffer pool as + /// this deque. + pub fn pool<'a>(&'a mut self) -> &'a mut BufferPool { + unsafe { &mut (*self.deque.get()).pool } + } +} + +impl Clone for Stealer { + fn clone(&self) -> Stealer { Stealer { deque: self.deque.clone() } } +} + +// Almost all of this code can be found directly in the paper so I'm not +// personally going to heavily comment what's going on here. + +impl Deque { + fn new(mut pool: BufferPool) -> Deque { + let buf = pool.alloc(MIN_BITS); + Deque { + bottom: AtomicInt::new(0), + top: AtomicInt::new(0), + array: AtomicPtr::new(unsafe { cast::transmute(buf) }), + pool: pool, + } + } + + unsafe fn push(&mut self, data: T) { + let mut b = self.bottom.load(SeqCst); + let t = self.top.load(SeqCst); + let mut a = self.array.load(SeqCst); + let size = b - t; + if size >= (*a).size() - 1 { + // You won't find this code in the chase-lev deque paper. This is + // alluded to in a small footnote, however. We always free a buffer + // when growing in order to prevent leaks. + a = self.swap_buffer(b, a, (*a).resize(b, t, 1)); + b = self.bottom.load(SeqCst); + } + (*a).put(b, data); + self.bottom.store(b + 1, SeqCst); + } + + unsafe fn pop(&mut self) -> Option { + let b = self.bottom.load(SeqCst); + let a = self.array.load(SeqCst); + let b = b - 1; + self.bottom.store(b, SeqCst); + let t = self.top.load(SeqCst); + let size = b - t; + if size < 0 { + self.bottom.store(t, SeqCst); + return None; + } + let data = (*a).get(b); + if size > 0 { + self.maybe_shrink(b, t); + return Some(data); + } + if self.top.compare_and_swap(t, t + 1, SeqCst) == t { + self.bottom.store(t + 1, SeqCst); + return Some(data); + } else { + self.bottom.store(t + 1, SeqCst); + cast::forget(data); // someone else stole this value + return None; + } + } + + unsafe fn steal(&mut self) -> Stolen { + let t = self.top.load(SeqCst); + let old = self.array.load(SeqCst); + let b = self.bottom.load(SeqCst); + let a = self.array.load(SeqCst); + let size = b - t; + if size <= 0 { return Empty } + if size % (*a).size() == 0 { + if a == old && t == self.top.load(SeqCst) { + return Empty + } + return Abort + } + let data = (*a).get(t); + if self.top.compare_and_swap(t, t + 1, SeqCst) == t { + Data(data) + } else { + cast::forget(data); // someone else stole this value + Abort + } + } + + unsafe fn maybe_shrink(&mut self, b: int, t: int) { + let a = self.array.load(SeqCst); + if b - t < (*a).size() / K && b - t > (1 << MIN_BITS) { + self.swap_buffer(b, a, (*a).resize(b, t, -1)); + } + } + + // Helper routine not mentioned in the paper which is used in growing and + // shrinking buffers to swap in a new buffer into place. As a bit of a + // recap, the whole point that we need a buffer pool rather than just + // calling malloc/free directly is that stealers can continue using buffers + // after this method has called 'free' on it. The continued usage is simply + // a read followed by a forget, but we must make sure that the memory can + // continue to be read after we flag this buffer for reclamation. + unsafe fn swap_buffer(&mut self, b: int, old: *mut Buffer, + buf: Buffer) -> *mut Buffer { + let newbuf: *mut Buffer = cast::transmute(~buf); + self.array.store(newbuf, SeqCst); + let ss = (*newbuf).size(); + self.bottom.store(b + ss, SeqCst); + let t = self.top.load(SeqCst); + if self.top.compare_and_swap(t, t + ss, SeqCst) != t { + self.bottom.store(b, SeqCst); + } + self.pool.free(cast::transmute(old)); + return newbuf; + } +} + + +#[unsafe_destructor] +impl Drop for Deque { + fn drop(&mut self) { + let t = self.top.load(SeqCst); + let b = self.bottom.load(SeqCst); + let a = self.array.load(SeqCst); + // Free whatever is leftover in the dequeue, and then move the buffer + // back into the pool. + for i in range(t, b) { + let _: T = unsafe { (*a).get(i) }; + } + self.pool.free(unsafe { cast::transmute(a) }); + } +} + +impl Buffer { + unsafe fn new(log_size: int) -> Buffer { + let size = (1 << log_size) * mem::size_of::(); + let buffer = libc::malloc(size as libc::size_t); + assert!(!buffer.is_null()); + Buffer { + storage: buffer as *T, + log_size: log_size, + } + } + + fn size(&self) -> int { 1 << self.log_size } + + // Apparently LLVM cannot optimize (foo % (1 << bar)) into this implicitly + fn mask(&self) -> int { (1 << self.log_size) - 1 } + + // This does not protect against loading duplicate values of the same cell, + // nor does this clear out the contents contained within. Hence, this is a + // very unsafe method which the caller needs to treat specially in case a + // race is lost. + unsafe fn get(&self, i: int) -> T { + ptr::read_ptr(self.storage.offset(i & self.mask())) + } + + // Unsafe because this unsafely overwrites possibly uninitialized or + // initialized data. + unsafe fn put(&mut self, i: int, t: T) { + let ptr = self.storage.offset(i & self.mask()); + ptr::copy_nonoverlapping_memory(ptr as *mut T, &t as *T, 1); + cast::forget(t); + } + + // Again, unsafe because this has incredibly dubious ownership violations. + // It is assumed that this buffer is immediately dropped. + unsafe fn resize(&self, b: int, t: int, delta: int) -> Buffer { + let mut buf = Buffer::new(self.log_size + delta); + for i in range(t, b) { + buf.put(i, self.get(i)); + } + return buf; + } +} + +#[unsafe_destructor] +impl Drop for Buffer { + fn drop(&mut self) { + // It is assumed that all buffers are empty on drop. + unsafe { libc::free(self.storage as *libc::c_void) } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::{Data, BufferPool, Abort, Empty, Worker, Stealer}; + + use cast; + use rt::thread::Thread; + use rand; + use rand::Rng; + use sync::atomics::{AtomicBool, INIT_ATOMIC_BOOL, SeqCst, + AtomicUint, INIT_ATOMIC_UINT}; + use vec; + + #[test] + fn smoke() { + let mut pool = BufferPool::new(); + let (mut w, mut s) = pool.deque(); + assert_eq!(w.pop(), None); + assert_eq!(s.steal(), Empty); + w.push(1); + assert_eq!(w.pop(), Some(1)); + w.push(1); + assert_eq!(s.steal(), Data(1)); + w.push(1); + assert_eq!(s.clone().steal(), Data(1)); + } + + #[test] + fn stealpush() { + static AMT: int = 100000; + let mut pool = BufferPool::::new(); + let (mut w, s) = pool.deque(); + let t = do Thread::start { + let mut s = s; + let mut left = AMT; + while left > 0 { + match s.steal() { + Data(i) => { + assert_eq!(i, 1); + left -= 1; + } + Abort | Empty => {} + } + } + }; + + for _ in range(0, AMT) { + w.push(1); + } + + t.join(); + } + + #[test] + fn stealpush_large() { + static AMT: int = 100000; + let mut pool = BufferPool::<(int, int)>::new(); + let (mut w, s) = pool.deque(); + let t = do Thread::start { + let mut s = s; + let mut left = AMT; + while left > 0 { + match s.steal() { + Data((1, 10)) => { left -= 1; } + Data(..) => fail!(), + Abort | Empty => {} + } + } + }; + + for _ in range(0, AMT) { + w.push((1, 10)); + } + + t.join(); + } + + fn stampede(mut w: Worker<~int>, s: Stealer<~int>, + nthreads: int, amt: uint) { + for _ in range(0, amt) { + w.push(~20); + } + let mut remaining = AtomicUint::new(amt); + let unsafe_remaining: *mut AtomicUint = &mut remaining; + + let threads = range(0, nthreads).map(|_| { + let s = s.clone(); + do Thread::start { + unsafe { + let mut s = s; + while (*unsafe_remaining).load(SeqCst) > 0 { + match s.steal() { + Data(~20) => { + (*unsafe_remaining).fetch_sub(1, SeqCst); + } + Data(..) => fail!(), + Abort | Empty => {} + } + } + } + } + }).to_owned_vec(); + + while remaining.load(SeqCst) > 0 { + match w.pop() { + Some(~20) => { remaining.fetch_sub(1, SeqCst); } + Some(..) => fail!(), + None => {} + } + } + + for thread in threads.move_iter() { + thread.join(); + } + } + + #[test] + fn run_stampede() { + let mut pool = BufferPool::<~int>::new(); + let (w, s) = pool.deque(); + stampede(w, s, 8, 10000); + } + + #[test] + fn many_stampede() { + static AMT: uint = 4; + let mut pool = BufferPool::<~int>::new(); + let threads = range(0, AMT).map(|_| { + let (w, s) = pool.deque(); + do Thread::start { + stampede(w, s, 4, 10000); + } + }).to_owned_vec(); + + for thread in threads.move_iter() { + thread.join(); + } + } + + #[test] + fn stress() { + static AMT: int = 100000; + static NTHREADS: int = 8; + static mut DONE: AtomicBool = INIT_ATOMIC_BOOL; + static mut HITS: AtomicUint = INIT_ATOMIC_UINT; + let mut pool = BufferPool::::new(); + let (mut w, s) = pool.deque(); + + let threads = range(0, NTHREADS).map(|_| { + let s = s.clone(); + do Thread::start { + unsafe { + let mut s = s; + loop { + match s.steal() { + Data(2) => { HITS.fetch_add(1, SeqCst); } + Data(..) => fail!(), + _ if DONE.load(SeqCst) => break, + _ => {} + } + } + } + } + }).to_owned_vec(); + + let mut rng = rand::task_rng(); + let mut expected = 0; + while expected < AMT { + if rng.gen_range(0, 3) == 2 { + match w.pop() { + None => {} + Some(2) => unsafe { HITS.fetch_add(1, SeqCst); }, + Some(_) => fail!(), + } + } else { + expected += 1; + w.push(2); + } + } + + unsafe { + while HITS.load(SeqCst) < AMT as uint { + match w.pop() { + None => {} + Some(2) => { HITS.fetch_add(1, SeqCst); }, + Some(_) => fail!(), + } + } + DONE.store(true, SeqCst); + } + + for thread in threads.move_iter() { + thread.join(); + } + + assert_eq!(unsafe { HITS.load(SeqCst) }, expected as uint); + } + + #[test] + #[ignore(cfg(windows))] // apparently windows scheduling is weird? + fn no_starvation() { + static AMT: int = 10000; + static NTHREADS: int = 4; + static mut DONE: AtomicBool = INIT_ATOMIC_BOOL; + let mut pool = BufferPool::<(int, uint)>::new(); + let (mut w, s) = pool.deque(); + + let (threads, hits) = vec::unzip(range(0, NTHREADS).map(|_| { + let s = s.clone(); + let unique_box = ~AtomicUint::new(0); + let thread_box = unsafe { + *cast::transmute::<&~AtomicUint,**mut AtomicUint>(&unique_box) + }; + (do Thread::start { + unsafe { + let mut s = s; + loop { + match s.steal() { + Data((1, 2)) => { + (*thread_box).fetch_add(1, SeqCst); + } + Data(..) => fail!(), + _ if DONE.load(SeqCst) => break, + _ => {} + } + } + } + }, unique_box) + })); + + let mut rng = rand::task_rng(); + let mut myhit = false; + let mut iter = 0; + 'outer: loop { + for _ in range(0, rng.gen_range(0, AMT)) { + if !myhit && rng.gen_range(0, 3) == 2 { + match w.pop() { + None => {} + Some((1, 2)) => myhit = true, + Some(_) => fail!(), + } + } else { + w.push((1, 2)); + } + } + iter += 1; + + debug!("loop iteration {}", iter); + for (i, slot) in hits.iter().enumerate() { + let amt = slot.load(SeqCst); + debug!("thread {}: {}", i, amt); + if amt == 0 { continue 'outer; } + } + if myhit { + break + } + } + + unsafe { DONE.store(true, SeqCst); } + + for thread in threads.move_iter() { + thread.join(); + } + } +} + diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs new file mode 100644 index 00000000000..3213c538152 --- /dev/null +++ b/src/libstd/sync/mod.rs @@ -0,0 +1,23 @@ +// 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. + +//! Useful synchronization primitives +//! +//! This modules contains useful safe and unsafe synchronization primitives. +//! Most of the primitives in this module do not provide any sort of locking +//! and/or blocking at all, but rather provide the necessary tools to build +//! other types of concurrent primitives. + +pub mod arc; +pub mod atomics; +pub mod deque; +pub mod mpmc_bounded_queue; +pub mod mpsc_queue; +pub mod spsc_queue; diff --git a/src/libstd/sync/mpmc_bounded_queue.rs b/src/libstd/sync/mpmc_bounded_queue.rs new file mode 100644 index 00000000000..b623976306d --- /dev/null +++ b/src/libstd/sync/mpmc_bounded_queue.rs @@ -0,0 +1,211 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +#[allow(missing_doc, dead_code)]; + +// http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue + +use clone::Clone; +use kinds::Send; +use num::{Exponential,Algebraic,Round}; +use option::{Option, Some, None}; +use sync::arc::UnsafeArc; +use sync::atomics::{AtomicUint,Relaxed,Release,Acquire}; +use vec; + +struct Node { + sequence: AtomicUint, + value: Option, +} + +struct State { + pad0: [u8, ..64], + buffer: ~[Node], + mask: uint, + pad1: [u8, ..64], + enqueue_pos: AtomicUint, + pad2: [u8, ..64], + dequeue_pos: AtomicUint, + pad3: [u8, ..64], +} + +pub struct Queue { + priv state: UnsafeArc>, +} + +impl State { + fn with_capacity(capacity: uint) -> State { + let capacity = if capacity < 2 || (capacity & (capacity - 1)) != 0 { + if capacity < 2 { + 2u + } else { + // use next power of 2 as capacity + 2f64.pow(&((capacity as f64).log2().ceil())) as uint + } + } else { + capacity + }; + let buffer = vec::from_fn(capacity, |i:uint| { + Node{sequence:AtomicUint::new(i),value:None} + }); + State{ + pad0: [0, ..64], + buffer: buffer, + mask: capacity-1, + pad1: [0, ..64], + enqueue_pos: AtomicUint::new(0), + pad2: [0, ..64], + dequeue_pos: AtomicUint::new(0), + pad3: [0, ..64], + } + } + + fn push(&mut self, value: T) -> bool { + let mask = self.mask; + let mut pos = self.enqueue_pos.load(Relaxed); + loop { + let node = &mut self.buffer[pos & mask]; + let seq = node.sequence.load(Acquire); + let diff: int = seq as int - pos as int; + + if diff == 0 { + let enqueue_pos = self.enqueue_pos.compare_and_swap(pos, pos+1, Relaxed); + if enqueue_pos == pos { + node.value = Some(value); + node.sequence.store(pos+1, Release); + break + } else { + pos = enqueue_pos; + } + } else if (diff < 0) { + return false + } else { + pos = self.enqueue_pos.load(Relaxed); + } + } + true + } + + fn pop(&mut self) -> Option { + let mask = self.mask; + let mut pos = self.dequeue_pos.load(Relaxed); + loop { + let node = &mut self.buffer[pos & mask]; + let seq = node.sequence.load(Acquire); + let diff: int = seq as int - (pos + 1) as int; + if diff == 0 { + let dequeue_pos = self.dequeue_pos.compare_and_swap(pos, pos+1, Relaxed); + if dequeue_pos == pos { + let value = node.value.take(); + node.sequence.store(pos + mask + 1, Release); + return value + } else { + pos = dequeue_pos; + } + } else if diff < 0 { + return None + } else { + pos = self.dequeue_pos.load(Relaxed); + } + } + } +} + +impl Queue { + pub fn with_capacity(capacity: uint) -> Queue { + Queue{ + state: UnsafeArc::new(State::with_capacity(capacity)) + } + } + + pub fn push(&mut self, value: T) -> bool { + unsafe { (*self.state.get()).push(value) } + } + + pub fn pop(&mut self) -> Option { + unsafe { (*self.state.get()).pop() } + } +} + +impl Clone for Queue { + fn clone(&self) -> Queue { + Queue { + state: self.state.clone() + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use option::*; + use task; + use super::Queue; + + #[test] + fn test() { + let nthreads = 8u; + let nmsgs = 1000u; + let mut q = Queue::with_capacity(nthreads*nmsgs); + assert_eq!(None, q.pop()); + + for _ in range(0, nthreads) { + let q = q.clone(); + do task::spawn_sched(task::SingleThreaded) { + let mut q = q; + for i in range(0, nmsgs) { + assert!(q.push(i)); + } + } + } + + let mut completion_ports = ~[]; + for _ in range(0, nthreads) { + let (completion_port, completion_chan) = Chan::new(); + completion_ports.push(completion_port); + let q = q.clone(); + do task::spawn_sched(task::SingleThreaded) { + let mut q = q; + let mut i = 0u; + loop { + match q.pop() { + None => {}, + Some(_) => { + i += 1; + if i == nmsgs { break } + } + } + } + completion_chan.send(i); + } + } + + for completion_port in completion_ports.mut_iter() { + assert_eq!(nmsgs, completion_port.recv()); + } + } +} diff --git a/src/libstd/sync/mpsc_queue.rs b/src/libstd/sync/mpsc_queue.rs new file mode 100644 index 00000000000..89e56e3fa67 --- /dev/null +++ b/src/libstd/sync/mpsc_queue.rs @@ -0,0 +1,245 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +//! A mostly lock-free multi-producer, single consumer queue. +//! +//! This module contains an implementation of a concurrent MPSC queue. This +//! queue can be used to share data between tasks, and is also used as the +//! building block of channels in rust. +//! +//! Note that the current implementation of this queue has a caveat of the `pop` +//! method, and see the method for more information about it. Due to this +//! caveat, this queue may not be appropriate for all use-cases. + +// http://www.1024cores.net/home/lock-free-algorithms +// /queues/non-intrusive-mpsc-node-based-queue + +use cast; +use clone::Clone; +use kinds::Send; +use ops::Drop; +use option::{Option, None, Some}; +use ptr::RawPtr; +use sync::arc::UnsafeArc; +use sync::atomics::{AtomicPtr, Release, Acquire, AcqRel, Relaxed}; + +/// A result of the `pop` function. +pub enum PopResult { + /// Some data has been popped + Data(T), + /// The queue is empty + Empty, + /// The queue is in an inconsistent state. Popping data should succeed, but + /// some pushers have yet to make enough progress in order allow a pop to + /// succeed. It is recommended that a pop() occur "in the near future" in + /// order to see if the sender has made progress or not + Inconsistent, +} + +struct Node { + next: AtomicPtr>, + value: Option, +} + +struct State { + head: AtomicPtr>, + tail: *mut Node, + packet: P, +} + +/// The consumer half of this concurrent queue. This half is used to receive +/// data from the producers. +pub struct Consumer { + priv state: UnsafeArc>, +} + +/// The production half of the concurrent queue. This handle may be cloned in +/// order to make handles for new producers. +pub struct Producer { + priv state: UnsafeArc>, +} + +impl Clone for Producer { + fn clone(&self) -> Producer { + Producer { state: self.state.clone() } + } +} + +/// Creates a new MPSC queue. The given argument `p` is a user-defined "packet" +/// of information which will be shared by the consumer and the producer which +/// can be re-acquired via the `packet` function. This is helpful when extra +/// state is shared between the producer and consumer, but note that there is no +/// synchronization performed of this data. +pub fn queue(p: P) -> (Consumer, Producer) { + unsafe { + let (a, b) = UnsafeArc::new2(State::new(p)); + (Consumer { state: a }, Producer { state: b }) + } +} + +impl Node { + unsafe fn new(v: Option) -> *mut Node { + cast::transmute(~Node { + next: AtomicPtr::new(0 as *mut Node), + value: v, + }) + } +} + +impl State { + unsafe fn new(p: P) -> State { + let stub = Node::new(None); + State { + head: AtomicPtr::new(stub), + tail: stub, + packet: p, + } + } + + unsafe fn push(&mut self, t: T) { + let n = Node::new(Some(t)); + let prev = self.head.swap(n, AcqRel); + (*prev).next.store(n, Release); + } + + unsafe fn pop(&mut self) -> PopResult { + let tail = self.tail; + let next = (*tail).next.load(Acquire); + + if !next.is_null() { + self.tail = next; + assert!((*tail).value.is_none()); + assert!((*next).value.is_some()); + let ret = (*next).value.take_unwrap(); + let _: ~Node = cast::transmute(tail); + return Data(ret); + } + + if self.head.load(Acquire) == tail {Empty} else {Inconsistent} + } +} + +#[unsafe_destructor] +impl Drop for State { + fn drop(&mut self) { + unsafe { + let mut cur = self.tail; + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _: ~Node = cast::transmute(cur); + cur = next; + } + } + } +} + +impl Producer { + /// Pushes a new value onto this queue. + pub fn push(&mut self, value: T) { + unsafe { (*self.state.get()).push(value) } + } + /// Gets an unsafe pointer to the user-defined packet shared by the + /// producers and the consumer. Note that care must be taken to ensure that + /// the lifetime of the queue outlives the usage of the returned pointer. + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +impl Consumer { + /// Pops some data from this queue. + /// + /// Note that the current implementation means that this function cannot + /// return `Option`. It is possible for this queue to be in an + /// inconsistent state where many pushes have suceeded and completely + /// finished, but pops cannot return `Some(t)`. This inconsistent state + /// happens when a pusher is pre-empted at an inopportune moment. + /// + /// This inconsistent state means that this queue does indeed have data, but + /// it does not currently have access to it at this time. + pub fn pop(&mut self) -> PopResult { + unsafe { (*self.state.get()).pop() } + } + /// Attempts to pop data from this queue, but doesn't attempt too hard. This + /// will canonicalize inconsistent states to a `None` value. + pub fn casual_pop(&mut self) -> Option { + match self.pop() { + Data(t) => Some(t), + Empty | Inconsistent => None, + } + } + /// Gets an unsafe pointer to the underlying user-defined packet. See + /// `Producer.packet` for more information. + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use task; + use super::{queue, Data, Empty, Inconsistent}; + + #[test] + fn test_full() { + let (_, mut p) = queue(()); + p.push(~1); + p.push(~2); + } + + #[test] + fn test() { + let nthreads = 8u; + let nmsgs = 1000u; + let (mut c, p) = queue(()); + match c.pop() { + Empty => {} + Inconsistent | Data(..) => fail!() + } + + for _ in range(0, nthreads) { + let q = p.clone(); + do task::spawn_sched(task::SingleThreaded) { + let mut q = q; + for i in range(0, nmsgs) { + q.push(i); + } + } + } + + let mut i = 0u; + while i < nthreads * nmsgs { + match c.pop() { + Empty | Inconsistent => {}, + Data(_) => { i += 1 } + } + } + } +} + diff --git a/src/libstd/sync/spsc_queue.rs b/src/libstd/sync/spsc_queue.rs new file mode 100644 index 00000000000..c4abba04659 --- /dev/null +++ b/src/libstd/sync/spsc_queue.rs @@ -0,0 +1,334 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue + +//! A single-producer single-consumer concurrent queue +//! +//! This module contains the implementation of an SPSC queue which can be used +//! concurrently between two tasks. This data structure is safe to use and +//! enforces the semantics that there is one pusher and one popper. + +use cast; +use kinds::Send; +use ops::Drop; +use option::{Some, None, Option}; +use ptr::RawPtr; +use sync::arc::UnsafeArc; +use sync::atomics::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release}; + +// Node within the linked list queue of messages to send +struct Node { + // XXX: this could be an uninitialized T if we're careful enough, and + // that would reduce memory usage (and be a bit faster). + // is it worth it? + value: Option, // nullable for re-use of nodes + next: AtomicPtr>, // next node in the queue +} + +// The producer/consumer halves both need access to the `tail` field, and if +// they both have access to that we may as well just give them both access +// to this whole structure. +struct State { + // consumer fields + tail: *mut Node, // where to pop from + tail_prev: AtomicPtr>, // where to pop from + + // producer fields + head: *mut Node, // where to push to + first: *mut Node, // where to get new nodes from + tail_copy: *mut Node, // between first/tail + + // Cache maintenance fields. Additions and subtractions are stored + // separately in order to allow them to use nonatomic addition/subtraction. + cache_bound: uint, + cache_additions: AtomicUint, + cache_subtractions: AtomicUint, + + packet: P, +} + +/// Producer half of this queue. This handle is used to push data to the +/// consumer. +pub struct Producer { + priv state: UnsafeArc>, +} + +/// Consumer half of this queue. This handle is used to receive data from the +/// producer. +pub struct Consumer { + priv state: UnsafeArc>, +} + +/// Creates a new queue. The producer returned is connected to the consumer to +/// push all data to the consumer. +/// +/// # Arguments +/// +/// * `bound` - This queue implementation is implemented with a linked list, +/// and this means that a push is always a malloc. In order to +/// amortize this cost, an internal cache of nodes is maintained +/// to prevent a malloc from always being necessary. This bound is +/// the limit on the size of the cache (if desired). If the value +/// is 0, then the cache has no bound. Otherwise, the cache will +/// never grow larger than `bound` (although the queue itself +/// could be much larger. +/// +/// * `p` - This is the user-defined packet of data which will also be shared +/// between the producer and consumer. +pub fn queue(bound: uint, + p: P) -> (Consumer, Producer) +{ + let n1 = Node::new(); + let n2 = Node::new(); + unsafe { (*n1).next.store(n2, Relaxed) } + let state = State { + tail: n2, + tail_prev: AtomicPtr::new(n1), + head: n2, + first: n1, + tail_copy: n1, + cache_bound: bound, + cache_additions: AtomicUint::new(0), + cache_subtractions: AtomicUint::new(0), + packet: p, + }; + let (arc1, arc2) = UnsafeArc::new2(state); + (Consumer { state: arc1 }, Producer { state: arc2 }) +} + +impl Node { + fn new() -> *mut Node { + unsafe { + cast::transmute(~Node { + value: None, + next: AtomicPtr::new(0 as *mut Node), + }) + } + } +} + +impl Producer { + /// Pushes data onto the queue + pub fn push(&mut self, t: T) { + unsafe { (*self.state.get()).push(t) } + } + /// Tests whether the queue is empty. Note that if this function returns + /// `false`, the return value is significant, but if the return value is + /// `true` then almost no meaning can be attached to the return value. + pub fn is_empty(&self) -> bool { + unsafe { (*self.state.get()).is_empty() } + } + /// Acquires an unsafe pointer to the underlying user-defined packet. Note + /// that care must be taken to ensure that the queue outlives the usage of + /// the packet (because it is an unsafe pointer). + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +impl Consumer { + /// Pops some data from this queue, returning `None` when the queue is + /// empty. + pub fn pop(&mut self) -> Option { + unsafe { (*self.state.get()).pop() } + } + /// Same function as the producer's `packet` method. + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +impl State { + // remember that there is only one thread executing `push` (and only one + // thread executing `pop`) + unsafe fn push(&mut self, t: T) { + // Acquire a node (which either uses a cached one or allocates a new + // one), and then append this to the 'head' node. + let n = self.alloc(); + assert!((*n).value.is_none()); + (*n).value = Some(t); + (*n).next.store(0 as *mut Node, Relaxed); + (*self.head).next.store(n, Release); + self.head = n; + } + + unsafe fn alloc(&mut self) -> *mut Node { + // First try to see if we can consume the 'first' node for our uses. + // We try to avoid as many atomic instructions as possible here, so + // the addition to cache_subtractions is not atomic (plus we're the + // only one subtracting from the cache). + if self.first != self.tail_copy { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = self.first; + self.first = (*ret).next.load(Relaxed); + return ret; + } + // If the above fails, then update our copy of the tail and try + // again. + self.tail_copy = self.tail_prev.load(Acquire); + if self.first != self.tail_copy { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = self.first; + self.first = (*ret).next.load(Relaxed); + return ret; + } + // If all of that fails, then we have to allocate a new node + // (there's nothing in the node cache). + Node::new() + } + + // remember that there is only one thread executing `pop` (and only one + // thread executing `push`) + unsafe fn pop(&mut self) -> Option { + // The `tail` node is not actually a used node, but rather a + // sentinel from where we should start popping from. Hence, look at + // tail's next field and see if we can use it. If we do a pop, then + // the current tail node is a candidate for going into the cache. + let tail = self.tail; + let next = (*tail).next.load(Acquire); + if next.is_null() { return None } + assert!((*next).value.is_some()); + let ret = (*next).value.take(); + + self.tail = next; + if self.cache_bound == 0 { + self.tail_prev.store(tail, Release); + } else { + // XXX: this is dubious with overflow. + let additions = self.cache_additions.load(Relaxed); + let subtractions = self.cache_subtractions.load(Relaxed); + let size = additions - subtractions; + + if size < self.cache_bound { + self.tail_prev.store(tail, Release); + self.cache_additions.store(additions + 1, Relaxed); + } else { + (*self.tail_prev.load(Relaxed)).next.store(next, Relaxed); + // We have successfully erased all references to 'tail', so + // now we can safely drop it. + let _: ~Node = cast::transmute(tail); + } + } + return ret; + } + + unsafe fn is_empty(&self) -> bool { + let tail = self.tail; + let next = (*tail).next.load(Acquire); + return next.is_null(); + } +} + +#[unsafe_destructor] +impl Drop for State { + fn drop(&mut self) { + unsafe { + let mut cur = self.first; + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _n: ~Node = cast::transmute(cur); + cur = next; + } + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::queue; + use task; + + #[test] + fn smoke() { + let (mut c, mut p) = queue(0, ()); + p.push(1); + p.push(2); + assert_eq!(c.pop(), Some(1)); + assert_eq!(c.pop(), Some(2)); + assert_eq!(c.pop(), None); + p.push(3); + p.push(4); + assert_eq!(c.pop(), Some(3)); + assert_eq!(c.pop(), Some(4)); + assert_eq!(c.pop(), None); + } + + #[test] + fn drop_full() { + let (_, mut p) = queue(0, ()); + p.push(~1); + p.push(~2); + } + + #[test] + fn smoke_bound() { + let (mut c, mut p) = queue(1, ()); + p.push(1); + p.push(2); + assert_eq!(c.pop(), Some(1)); + assert_eq!(c.pop(), Some(2)); + assert_eq!(c.pop(), None); + p.push(3); + p.push(4); + assert_eq!(c.pop(), Some(3)); + assert_eq!(c.pop(), Some(4)); + assert_eq!(c.pop(), None); + } + + #[test] + fn stress() { + stress_bound(0); + stress_bound(1); + + fn stress_bound(bound: uint) { + let (c, mut p) = queue(bound, ()); + do task::spawn_sched(task::SingleThreaded) { + let mut c = c; + for _ in range(0, 100000) { + loop { + match c.pop() { + Some(1) => break, + Some(_) => fail!(), + None => {} + } + } + } + } + for _ in range(0, 100000) { + p.push(1); + } + } + } +} -- cgit 1.4.1-3-g733a5 From 018d60509c04cdebdf8b0d9e2b58f2604538e516 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Dec 2013 21:38:57 -0800 Subject: std: Get stdtest all passing again This commit brings the library up-to-date in order to get all tests passing again --- mk/host.mk | 2 +- mk/tests.mk | 4 +- src/driver/driver.rs | 2 + src/libgreen/lib.rs | 2 + src/libnative/task.rs | 11 +- src/libstd/any.rs | 15 +- src/libstd/comm/mod.rs | 412 +++++++++++++++------------------- src/libstd/comm/select.rs | 94 ++------ src/libstd/io/fs.rs | 87 +++---- src/libstd/io/mod.rs | 4 + src/libstd/io/net/tcp.rs | 62 +++-- src/libstd/io/net/udp.rs | 11 +- src/libstd/io/net/unix.rs | 5 +- src/libstd/io/stdio.rs | 17 +- src/libstd/io/test.rs | 44 +++- src/libstd/lib.rs | 8 +- src/libstd/local_data.rs | 1 + src/libstd/rt/local.rs | 19 +- src/libstd/rt/task.rs | 94 ++++---- src/libstd/run.rs | 4 +- src/libstd/sync/arc.rs | 1 - src/libstd/sync/mpmc_bounded_queue.rs | 12 +- src/libstd/sync/mpsc_queue.rs | 10 +- src/libstd/sync/spsc_queue.rs | 7 +- src/libstd/task.rs | 98 +++----- src/libstd/unstable/mutex.rs | 2 +- src/libstd/unstable/stack.rs | 9 +- src/libstd/unstable/sync.rs | 3 +- src/libstd/vec.rs | 1 - 29 files changed, 451 insertions(+), 590 deletions(-) (limited to 'src/libstd/sync') diff --git a/mk/host.mk b/mk/host.mk index 7aabff52bc4..f94afe587f3 100644 --- a/mk/host.mk +++ b/mk/host.mk @@ -24,7 +24,7 @@ define CP_HOST_STAGE_N # Note: $(3) and $(4) are both the same! $$(HBIN$(2)_H_$(4))/rustc$$(X_$(4)): \ - $$(TBIN$(1)_T_$(4)_H_$(3))/rustc$$(X_$(4)) + $$(TBIN$(1)_T_$(4)_H_$(3))/rustc$$(X_$(4)) \ $$(HLIBRUSTC_DEFAULT$(2)_H_$(4)) \ | $$(HBIN$(2)_H_$(4))/ @$$(call E, cp: $$@) diff --git a/mk/tests.mk b/mk/tests.mk index 179e41ad330..9fd9d9617c7 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -348,13 +348,13 @@ STDTESTDEP_$(1)_$(2)_$(3) = endif $(3)/stage$(1)/test/stdtest-$(2)$$(X_$(2)): \ - $$(STDLIB_CRATE) $$(STDLIB_INPUTS) \ + $$(STDLIB_CRATE) $$(STDLIB_INPUTS) \ $$(STDTESTDEP_$(1)_$(2)_$(3)) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test $(3)/stage$(1)/test/extratest-$(2)$$(X_$(2)): \ - $$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \ + $$(EXTRALIB_CRATE) $$(EXTRALIB_INPUTS) \ $$(STDTESTDEP_$(1)_$(2)_$(3)) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 9402578d552..8e5b6356a0b 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[cfg(stage0)] extern mod green; + #[cfg(rustpkg)] extern mod this = "rustpkg"; diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 193b64ff7e5..6530316a627 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -57,6 +57,8 @@ pub mod sleeper_list; pub mod stack; pub mod task; +#[cfg(test)] mod tests; + #[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { diff --git a/src/libnative/task.rs b/src/libnative/task.rs index fa7500ca85e..782bef10c92 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -22,7 +22,7 @@ use std::rt::task::{Task, BlockedTask}; use std::rt::thread::Thread; use std::rt; use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; -use std::task::TaskOpts; +use std::task::{TaskOpts, default_task_opts}; use std::unstable::mutex::{Mutex, MUTEX_INIT}; use std::unstable::stack; @@ -73,9 +73,14 @@ pub fn new() -> ~Task { return task; } +/// Spawns a function with the default configuration +pub fn spawn(f: proc()) { + spawn_opts(default_task_opts(), f) +} + /// Spawns a new task given the configuration options and a procedure to run /// inside the task. -pub fn spawn(opts: TaskOpts, f: proc()) { +pub fn spawn_opts(opts: TaskOpts, f: proc()) { // must happen before the spawn, no need to synchronize with a lock. unsafe { THREAD_CNT.fetch_add(1, SeqCst); } @@ -238,7 +243,7 @@ impl rt::Runtime for Ops { cur_task.put_runtime(self as ~rt::Runtime); Local::put(cur_task); - task::spawn(opts, f); + task::spawn_opts(opts, f); } fn local_io<'a>(&'a mut self) -> Option> { diff --git a/src/libstd/any.rs b/src/libstd/any.rs index 49bae30a461..45a91d01b7a 100644 --- a/src/libstd/any.rs +++ b/src/libstd/any.rs @@ -119,7 +119,7 @@ impl<'a> AnyMutRefExt<'a> for &'a mut Any { /// Extension methods for a owning `Any` trait object pub trait AnyOwnExt { /// Returns the boxed value if it is of type `T`, or - /// `None` if it isn't. + /// `Err(Self)` if it isn't. fn move(self) -> Result<~T, Self>; } @@ -156,9 +156,8 @@ impl<'a> ToStr for &'a Any { #[cfg(test)] mod tests { + use prelude::*; use super::*; - use super::AnyRefExt; - use option::{Some, None}; #[deriving(Eq)] struct Test; @@ -385,8 +384,14 @@ mod tests { let a = ~8u as ~Any; let b = ~Test as ~Any; - assert_eq!(a.move(), Ok(~8u)); - assert_eq!(b.move(), Ok(~Test)); + match a.move::() { + Ok(a) => { assert_eq!(a, ~8u); } + Err(..) => fail!() + } + match b.move::() { + Ok(a) => { assert_eq!(a, ~Test); } + Err(..) => fail!() + } let a = ~8u as ~Any; let b = ~Test as ~Any; diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index f5048ec62a4..76a9e5d17e1 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -251,18 +251,21 @@ macro_rules! test ( mod $name { #[allow(unused_imports)]; - use util; - use super::super::*; + use native; use prelude::*; + use super::*; + use super::super::*; + use task; + use util; fn f() $b $($a)* #[test] fn uv() { f() } - $($a)* #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn native() { - use unstable::run_in_bare_thread; - run_in_bare_thread(f); + $($a)* #[test] fn native() { + use native; + let (p, c) = Chan::new(); + do native::task::spawn { c.send(f()) } + p.recv(); } } ) @@ -889,10 +892,16 @@ impl Drop for Port { mod test { use prelude::*; - use task; - use rt::thread::Thread; + use native; + use os; use super::*; - use rt::test::*; + + pub fn stress_factor() -> uint { + match os::getenv("RUST_TEST_STRESS") { + Some(val) => from_str::(val).unwrap(), + None => 1, + } + } test!(fn smoke() { let (p, c) = Chan::new(); @@ -919,99 +928,88 @@ mod test { assert_eq!(p.recv(), 1); }) - #[test] - fn smoke_threads() { + test!(fn smoke_threads() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { c.send(1); } assert_eq!(p.recv(), 1); - } + }) - #[test] #[should_fail] - fn smoke_port_gone() { + test!(fn smoke_port_gone() { let (p, c) = Chan::new(); drop(p); c.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_shared_port_gone() { + test!(fn smoke_shared_port_gone() { let (p, c) = SharedChan::new(); drop(p); c.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_shared_port_gone2() { + test!(fn smoke_shared_port_gone2() { let (p, c) = SharedChan::new(); drop(p); let c2 = c.clone(); drop(c); c2.send(1); - } + } #[should_fail]) - #[test] #[should_fail] - fn port_gone_concurrent() { + test!(fn port_gone_concurrent() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { p.recv(); } loop { c.send(1) } - } + } #[should_fail]) - #[test] #[should_fail] - fn port_gone_concurrent_shared() { + test!(fn port_gone_concurrent_shared() { let (p, c) = SharedChan::new(); let c1 = c.clone(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { p.recv(); } loop { c.send(1); c1.send(1); } - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_chan_gone() { + test!(fn smoke_chan_gone() { let (p, c) = Chan::::new(); drop(c); p.recv(); - } + } #[should_fail]) - #[test] #[should_fail] - fn smoke_chan_gone_shared() { + test!(fn smoke_chan_gone_shared() { let (p, c) = SharedChan::<()>::new(); let c2 = c.clone(); drop(c); drop(c2); p.recv(); - } + } #[should_fail]) - #[test] #[should_fail] - fn chan_gone_concurrent() { + test!(fn chan_gone_concurrent() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { c.send(1); c.send(1); } loop { p.recv(); } - } + } #[should_fail]) - #[test] - fn stress() { + test!(fn stress() { let (p, c) = Chan::new(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { for _ in range(0, 10000) { c.send(1); } } for _ in range(0, 10000) { assert_eq!(p.recv(), 1); } - } + }) - #[test] - fn stress_shared() { + test!(fn stress_shared() { static AMT: uint = 10000; static NTHREADS: uint = 8; let (p, c) = SharedChan::::new(); @@ -1027,47 +1025,53 @@ mod test { for _ in range(0, NTHREADS) { let c = c.clone(); - do task::spawn_sched(task::SingleThreaded) { + do spawn { for _ in range(0, AMT) { c.send(1); } } } p1.recv(); - - } + }) #[test] #[ignore(cfg(windows))] // FIXME(#11003) fn send_from_outside_runtime() { let (p, c) = Chan::::new(); let (p1, c1) = Chan::new(); + let (port, chan) = SharedChan::new(); + let chan2 = chan.clone(); do spawn { c1.send(()); for _ in range(0, 40) { assert_eq!(p.recv(), 1); } + chan2.send(()); } p1.recv(); - let t = do Thread::start { + do native::task::spawn { for _ in range(0, 40) { c.send(1); } - }; - t.join(); + chan.send(()); + } + port.recv(); + port.recv(); } #[test] #[ignore(cfg(windows))] // FIXME(#11003) fn recv_from_outside_runtime() { let (p, c) = Chan::::new(); - let t = do Thread::start { + let (dp, dc) = Chan::new(); + do native::task::spawn { for _ in range(0, 40) { assert_eq!(p.recv(), 1); } + dc.send(()); }; for _ in range(0, 40) { c.send(1); } - t.join(); + dp.recv(); } #[test] @@ -1075,173 +1079,132 @@ mod test { fn no_runtime() { let (p1, c1) = Chan::::new(); let (p2, c2) = Chan::::new(); - let t1 = do Thread::start { + let (port, chan) = SharedChan::new(); + let chan2 = chan.clone(); + do native::task::spawn { assert_eq!(p1.recv(), 1); c2.send(2); - }; - let t2 = do Thread::start { + chan2.send(()); + } + do native::task::spawn { c1.send(1); assert_eq!(p2.recv(), 2); - }; - t1.join(); - t2.join(); + chan.send(()); + } + port.recv(); + port.recv(); } - #[test] - fn oneshot_single_thread_close_port_first() { + test!(fn oneshot_single_thread_close_port_first() { // Simple test of closing without sending - do run_in_newsched_task { - let (port, _chan) = Chan::::new(); - { let _p = port; } - } - } + let (port, _chan) = Chan::::new(); + { let _p = port; } + }) - #[test] - fn oneshot_single_thread_close_chan_first() { + test!(fn oneshot_single_thread_close_chan_first() { // Simple test of closing without sending - do run_in_newsched_task { - let (_port, chan) = Chan::::new(); - { let _c = chan; } - } - } + let (_port, chan) = Chan::::new(); + { let _c = chan; } + }) - #[test] #[should_fail] - fn oneshot_single_thread_send_port_close() { + test!(fn oneshot_single_thread_send_port_close() { // Testing that the sender cleans up the payload if receiver is closed let (port, chan) = Chan::<~int>::new(); { let _p = port; } chan.send(~0); - } + } #[should_fail]) - #[test] - fn oneshot_single_thread_recv_chan_close() { + test!(fn oneshot_single_thread_recv_chan_close() { // Receiving on a closed chan will fail - do run_in_newsched_task { - let res = do spawntask_try { - let (port, chan) = Chan::<~int>::new(); - { let _c = chan; } - port.recv(); - }; - // What is our res? - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - do run_in_newsched_task { + let res = do task::try { let (port, chan) = Chan::<~int>::new(); - chan.send(~10); - assert!(port.recv() == ~10); - } - } + { let _c = chan; } + port.recv(); + }; + // What is our res? + assert!(res.is_err()); + }) - #[test] - fn oneshot_single_thread_try_send_open() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - assert!(chan.try_send(10)); - assert!(port.recv() == 10); - } - } + test!(fn oneshot_single_thread_send_then_recv() { + let (port, chan) = Chan::<~int>::new(); + chan.send(~10); + assert!(port.recv() == ~10); + }) - #[test] - fn oneshot_single_thread_try_send_closed() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _p = port; } - assert!(!chan.try_send(10)); - } - } + test!(fn oneshot_single_thread_try_send_open() { + let (port, chan) = Chan::::new(); + assert!(chan.try_send(10)); + assert!(port.recv() == 10); + }) - #[test] - fn oneshot_single_thread_try_recv_open() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - chan.send(10); - assert!(port.try_recv() == Some(10)); - } - } + test!(fn oneshot_single_thread_try_send_closed() { + let (port, chan) = Chan::::new(); + { let _p = port; } + assert!(!chan.try_send(10)); + }) - #[test] - fn oneshot_single_thread_try_recv_closed() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _c = chan; } - assert!(port.recv_opt() == None); - } - } + test!(fn oneshot_single_thread_try_recv_open() { + let (port, chan) = Chan::::new(); + chan.send(10); + assert!(port.try_recv() == Some(10)); + }) - #[test] - fn oneshot_single_thread_peek_data() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - assert!(port.try_recv().is_none()); - chan.send(10); - assert!(port.try_recv().is_some()); - } - } + test!(fn oneshot_single_thread_try_recv_closed() { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.recv_opt() == None); + }) - #[test] - fn oneshot_single_thread_peek_close() { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - { let _c = chan; } - assert!(port.try_recv().is_none()); - assert!(port.try_recv().is_none()); - } - } + test!(fn oneshot_single_thread_peek_data() { + let (port, chan) = Chan::::new(); + assert!(port.try_recv().is_none()); + chan.send(10); + assert!(port.try_recv().is_some()); + }) - #[test] - fn oneshot_single_thread_peek_open() { - do run_in_newsched_task { - let (port, _) = Chan::::new(); - assert!(port.try_recv().is_none()); - } - } + test!(fn oneshot_single_thread_peek_close() { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.try_recv().is_none()); + assert!(port.try_recv().is_none()); + }) - #[test] - fn oneshot_multi_task_recv_then_send() { - do run_in_newsched_task { - let (port, chan) = Chan::<~int>::new(); - do spawntask { - assert!(port.recv() == ~10); - } + test!(fn oneshot_single_thread_peek_open() { + let (port, _) = Chan::::new(); + assert!(port.try_recv().is_none()); + }) - chan.send(~10); + test!(fn oneshot_multi_task_recv_then_send() { + let (port, chan) = Chan::<~int>::new(); + do spawn { + assert!(port.recv() == ~10); } - } - #[test] - fn oneshot_multi_task_recv_then_close() { - do run_in_newsched_task { - let (port, chan) = Chan::<~int>::new(); - do spawntask_later { - let _chan = chan; - } - let res = do spawntask_try { - assert!(port.recv() == ~10); - }; - assert!(res.is_err()); + chan.send(~10); + }) + + test!(fn oneshot_multi_task_recv_then_close() { + let (port, chan) = Chan::<~int>::new(); + do spawn { + let _chan = chan; } - } + let res = do task::try { + assert!(port.recv() == ~10); + }; + assert!(res.is_err()); + }) - #[test] - fn oneshot_multi_thread_close_stress() { + test!(fn oneshot_multi_thread_close_stress() { stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = Chan::::new(); - let thread = do spawntask_thread { - let _p = port; - }; - let _chan = chan; - thread.join(); + let (port, chan) = Chan::::new(); + do spawn { + let _p = port; } + let _chan = chan; }) - } + }) - #[test] - fn oneshot_multi_thread_send_close_stress() { + test!(fn oneshot_multi_thread_send_close_stress() { stress_factor().times(|| { let (port, chan) = Chan::::new(); do spawn { @@ -1251,10 +1214,9 @@ mod test { chan.send(1); }; }) - } + }) - #[test] - fn oneshot_multi_thread_recv_close_stress() { + test!(fn oneshot_multi_thread_recv_close_stress() { stress_factor().times(|| { let (port, chan) = Chan::::new(); do spawn { @@ -1271,10 +1233,9 @@ mod test { } }; }) - } + }) - #[test] - fn oneshot_multi_thread_send_recv_stress() { + test!(fn oneshot_multi_thread_send_recv_stress() { stress_factor().times(|| { let (port, chan) = Chan::<~int>::new(); do spawn { @@ -1284,10 +1245,9 @@ mod test { assert!(port.recv() == ~10); } }) - } + }) - #[test] - fn stream_send_recv_stress() { + test!(fn stream_send_recv_stress() { stress_factor().times(|| { let (port, chan) = Chan::<~int>::new(); @@ -1297,7 +1257,7 @@ mod test { fn send(chan: Chan<~int>, i: int) { if i == 10 { return } - do spawntask_random { + do spawn { chan.send(~i); send(chan, i + 1); } @@ -1306,44 +1266,37 @@ mod test { fn recv(port: Port<~int>, i: int) { if i == 10 { return } - do spawntask_random { + do spawn { assert!(port.recv() == ~i); recv(port, i + 1); }; } }) - } + }) - #[test] - fn recv_a_lot() { + test!(fn recv_a_lot() { // Regression test that we don't run out of stack in scheduler context - do run_in_newsched_task { - let (port, chan) = Chan::new(); - 10000.times(|| { chan.send(()) }); - 10000.times(|| { port.recv() }); - } - } + let (port, chan) = Chan::new(); + 10000.times(|| { chan.send(()) }); + 10000.times(|| { port.recv() }); + }) - #[test] - fn shared_chan_stress() { - do run_in_mt_newsched_task { - let (port, chan) = SharedChan::new(); - let total = stress_factor() + 100; - total.times(|| { - let chan_clone = chan.clone(); - do spawntask_random { - chan_clone.send(()); - } - }); + test!(fn shared_chan_stress() { + let (port, chan) = SharedChan::new(); + let total = stress_factor() + 100; + total.times(|| { + let chan_clone = chan.clone(); + do spawn { + chan_clone.send(()); + } + }); - total.times(|| { - port.recv(); - }); - } - } + total.times(|| { + port.recv(); + }); + }) - #[test] - fn test_nested_recv_iter() { + test!(fn test_nested_recv_iter() { let (port, chan) = Chan::::new(); let (total_port, total_chan) = Chan::::new(); @@ -1360,10 +1313,9 @@ mod test { chan.send(2); drop(chan); assert_eq!(total_port.recv(), 6); - } + }) - #[test] - fn test_recv_iter_break() { + test!(fn test_recv_iter_break() { let (port, chan) = Chan::::new(); let (count_port, count_chan) = Chan::::new(); @@ -1385,5 +1337,5 @@ mod test { chan.try_send(2); drop(chan); assert_eq!(count_port.recv(), 4); - } + }) } diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs index 68e1a05a653..302c9d9ea46 100644 --- a/src/libstd/comm/select.rs +++ b/src/libstd/comm/select.rs @@ -51,11 +51,11 @@ use ops::Drop; use option::{Some, None, Option}; use ptr::RawPtr; use result::{Ok, Err}; -use rt::thread::Thread; use rt::local::Local; use rt::task::Task; use super::{Packet, Port}; use sync::atomics::{Relaxed, SeqCst}; +use task; use uint; macro_rules! select { @@ -310,6 +310,7 @@ impl Iterator<*mut Packet> for PacketIterator { } #[cfg(test)] +#[allow(unused_imports)] mod test { use super::super::*; use prelude::*; @@ -365,19 +366,16 @@ mod test { ) }) - #[test] - fn unblocks() { - use std::io::timer; - + test!(fn unblocks() { let (mut p1, c1) = Chan::::new(); let (mut p2, _c2) = Chan::::new(); let (p3, c3) = Chan::::new(); do spawn { - timer::sleep(3); + 20.times(task::deschedule); c1.send(1); p3.recv(); - timer::sleep(3); + 20.times(task::deschedule); } select! ( @@ -389,18 +387,15 @@ mod test { a = p1.recv_opt() => { assert_eq!(a, None); }, _b = p2.recv() => { fail!() } ) - } - - #[test] - fn both_ready() { - use std::io::timer; + }) + test!(fn both_ready() { let (mut p1, c1) = Chan::::new(); let (mut p2, c2) = Chan::::new(); let (p3, c3) = Chan::<()>::new(); do spawn { - timer::sleep(3); + 20.times(task::deschedule); c1.send(1); c2.send(2); p3.recv(); @@ -414,11 +409,12 @@ mod test { a = p1.recv() => { assert_eq!(a, 1); }, a = p2.recv() => { assert_eq!(a, 2); } ) + assert_eq!(p1.try_recv(), None); + assert_eq!(p2.try_recv(), None); c3.send(()); - } + }) - #[test] - fn stress() { + test!(fn stress() { static AMT: int = 10000; let (mut p1, c1) = Chan::::new(); let (mut p2, c2) = Chan::::new(); @@ -442,69 +438,5 @@ mod test { ) c3.send(()); } - } - - #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn stress_native() { - use std::rt::thread::Thread; - use std::unstable::run_in_bare_thread; - static AMT: int = 10000; - - do run_in_bare_thread { - let (mut p1, c1) = Chan::::new(); - let (mut p2, c2) = Chan::::new(); - let (p3, c3) = Chan::<()>::new(); - - let t = do Thread::start { - for i in range(0, AMT) { - if i % 2 == 0 { - c1.send(i); - } else { - c2.send(i); - } - p3.recv(); - } - }; - - for i in range(0, AMT) { - select! ( - i1 = p1.recv() => { assert!(i % 2 == 0 && i == i1); }, - i2 = p2.recv() => { assert!(i % 2 == 1 && i == i2); } - ) - c3.send(()); - } - t.join(); - } - } - - #[test] - #[ignore(cfg(windows))] // FIXME(#11003) - fn native_both_ready() { - use std::rt::thread::Thread; - use std::unstable::run_in_bare_thread; - - do run_in_bare_thread { - let (mut p1, c1) = Chan::::new(); - let (mut p2, c2) = Chan::::new(); - let (p3, c3) = Chan::<()>::new(); - - let t = do Thread::start { - c1.send(1); - c2.send(2); - p3.recv(); - }; - - select! ( - a = p1.recv() => { assert_eq!(a, 1); }, - b = p2.recv() => { assert_eq!(b, 2); } - ) - select! ( - a = p1.recv() => { assert_eq!(a, 1); }, - b = p2.recv() => { assert_eq!(b, 2); } - ) - c3.send(()); - t.join(); - } - } + }) } diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index ded1d254f3f..b4838d534dc 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -714,7 +714,7 @@ mod test { } } - fn tmpdir() -> TempDir { + pub fn tmpdir() -> TempDir { use os; use rand; let ret = os::tmpdir().join(format!("rust-{}", rand::random::())); @@ -722,32 +722,7 @@ mod test { TempDir(ret) } - macro_rules! test ( - { fn $name:ident() $b:block } => ( - mod $name { - use prelude::*; - use io::{SeekSet, SeekCur, SeekEnd, io_error, Read, Open, - ReadWrite}; - use io; - use str; - use io::fs::{File, rmdir, mkdir, readdir, rmdir_recursive, - mkdir_recursive, copy, unlink, stat, symlink, link, - readlink, chmod, lstat, change_file_times}; - use io::fs::test::tmpdir; - use util; - - fn f() $b - - #[test] fn uv() { f() } - #[test] fn native() { - use rt::test::run_in_newsched_task; - run_in_newsched_task(f); - } - } - ) - ) - - test!(fn file_test_io_smoke_test() { + iotest!(fn file_test_io_smoke_test() { let message = "it's alright. have a good time"; let tmpdir = tmpdir(); let filename = &tmpdir.join("file_rt_io_file_test.txt"); @@ -767,7 +742,7 @@ mod test { unlink(filename); }) - test!(fn invalid_path_raises() { + iotest!(fn invalid_path_raises() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_that_does_not_exist.txt"); let mut called = false; @@ -780,7 +755,7 @@ mod test { assert!(called); }) - test!(fn file_test_iounlinking_invalid_path_should_raise_condition() { + iotest!(fn file_test_iounlinking_invalid_path_should_raise_condition() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt"); let mut called = false; @@ -790,7 +765,7 @@ mod test { assert!(called); }) - test!(fn file_test_io_non_positional_read() { + iotest!(fn file_test_io_non_positional_read() { let message: &str = "ten-four"; let mut read_mem = [0, .. 8]; let tmpdir = tmpdir(); @@ -815,7 +790,7 @@ mod test { assert_eq!(read_str, message); }) - test!(fn file_test_io_seek_and_tell_smoke_test() { + iotest!(fn file_test_io_seek_and_tell_smoke_test() { let message = "ten-four"; let mut read_mem = [0, .. 4]; let set_cursor = 4 as u64; @@ -841,7 +816,7 @@ mod test { assert_eq!(tell_pos_post_read, message.len() as u64); }) - test!(fn file_test_io_seek_and_write() { + iotest!(fn file_test_io_seek_and_write() { let initial_msg = "food-is-yummy"; let overwrite_msg = "-the-bar!!"; let final_msg = "foo-the-bar!!"; @@ -864,7 +839,7 @@ mod test { assert!(read_str == final_msg.to_owned()); }) - test!(fn file_test_io_seek_shakedown() { + iotest!(fn file_test_io_seek_shakedown() { use std::str; // 01234567890123 let initial_msg = "qwer-asdf-zxcv"; let chunk_one: &str = "qwer"; @@ -895,7 +870,7 @@ mod test { unlink(filename); }) - test!(fn file_test_stat_is_correct_on_is_file() { + iotest!(fn file_test_stat_is_correct_on_is_file() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_file.txt"); { @@ -908,7 +883,7 @@ mod test { unlink(filename); }) - test!(fn file_test_stat_is_correct_on_is_dir() { + iotest!(fn file_test_stat_is_correct_on_is_dir() { let tmpdir = tmpdir(); let filename = &tmpdir.join("file_stat_correct_on_is_dir"); mkdir(filename, io::UserRWX); @@ -917,7 +892,7 @@ mod test { rmdir(filename); }) - test!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { + iotest!(fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() { let tmpdir = tmpdir(); let dir = &tmpdir.join("fileinfo_false_on_dir"); mkdir(dir, io::UserRWX); @@ -925,7 +900,7 @@ mod test { rmdir(dir); }) - test!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { + iotest!(fn file_test_fileinfo_check_exists_before_and_after_file_creation() { let tmpdir = tmpdir(); let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt"); File::create(file).write(bytes!("foo")); @@ -934,7 +909,7 @@ mod test { assert!(!file.exists()); }) - test!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { + iotest!(fn file_test_directoryinfo_check_exists_before_and_after_mkdir() { let tmpdir = tmpdir(); let dir = &tmpdir.join("before_and_after_dir"); assert!(!dir.exists()); @@ -945,7 +920,7 @@ mod test { assert!(!dir.exists()); }) - test!(fn file_test_directoryinfo_readdir() { + iotest!(fn file_test_directoryinfo_readdir() { use std::str; let tmpdir = tmpdir(); let dir = &tmpdir.join("di_readdir"); @@ -976,11 +951,11 @@ mod test { rmdir(dir); }) - test!(fn recursive_mkdir_slash() { + iotest!(fn recursive_mkdir_slash() { mkdir_recursive(&Path::new("/"), io::UserRWX); }) - test!(fn unicode_path_is_dir() { + iotest!(fn unicode_path_is_dir() { assert!(Path::new(".").is_dir()); assert!(!Path::new("test/stdtest/fs.rs").is_dir()); @@ -998,7 +973,7 @@ mod test { assert!(filepath.exists()); }) - test!(fn unicode_path_exists() { + iotest!(fn unicode_path_exists() { assert!(Path::new(".").exists()); assert!(!Path::new("test/nonexistent-bogus-path").exists()); @@ -1010,7 +985,7 @@ mod test { assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists()); }) - test!(fn copy_file_does_not_exist() { + iotest!(fn copy_file_does_not_exist() { let from = Path::new("test/nonexistent-bogus-path"); let to = Path::new("test/other-bogus-path"); match io::result(|| copy(&from, &to)) { @@ -1022,7 +997,7 @@ mod test { } }) - test!(fn copy_file_ok() { + iotest!(fn copy_file_ok() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1035,7 +1010,7 @@ mod test { assert_eq!(input.stat().perm, out.stat().perm); }) - test!(fn copy_file_dst_dir() { + iotest!(fn copy_file_dst_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); @@ -1045,7 +1020,7 @@ mod test { } }) - test!(fn copy_file_dst_exists() { + iotest!(fn copy_file_dst_exists() { let tmpdir = tmpdir(); let input = tmpdir.join("in"); let output = tmpdir.join("out"); @@ -1058,7 +1033,7 @@ mod test { (bytes!("foo")).to_owned()); }) - test!(fn copy_file_src_dir() { + iotest!(fn copy_file_src_dir() { let tmpdir = tmpdir(); let out = tmpdir.join("out"); @@ -1068,7 +1043,7 @@ mod test { assert!(!out.exists()); }) - test!(fn copy_file_preserves_perm_bits() { + iotest!(fn copy_file_preserves_perm_bits() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1083,7 +1058,7 @@ mod test { }) #[cfg(not(windows))] // FIXME(#10264) operation not permitted? - test!(fn symlinks_work() { + iotest!(fn symlinks_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1098,14 +1073,14 @@ mod test { }) #[cfg(not(windows))] // apparently windows doesn't like symlinks - test!(fn symlink_noexist() { + iotest!(fn symlink_noexist() { let tmpdir = tmpdir(); // symlinks can point to things that don't exist symlink(&tmpdir.join("foo"), &tmpdir.join("bar")); assert!(readlink(&tmpdir.join("bar")).unwrap() == tmpdir.join("foo")); }) - test!(fn readlink_not_symlink() { + iotest!(fn readlink_not_symlink() { let tmpdir = tmpdir(); match io::result(|| readlink(&*tmpdir)) { Ok(..) => fail!("wanted a failure"), @@ -1113,7 +1088,7 @@ mod test { } }) - test!(fn links_work() { + iotest!(fn links_work() { let tmpdir = tmpdir(); let input = tmpdir.join("in.txt"); let out = tmpdir.join("out.txt"); @@ -1139,7 +1114,7 @@ mod test { } }) - test!(fn chmod_works() { + iotest!(fn chmod_works() { let tmpdir = tmpdir(); let file = tmpdir.join("in.txt"); @@ -1156,7 +1131,7 @@ mod test { chmod(&file, io::UserFile); }) - test!(fn sync_doesnt_kill_anything() { + iotest!(fn sync_doesnt_kill_anything() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); @@ -1169,7 +1144,7 @@ mod test { drop(file); }) - test!(fn truncate_works() { + iotest!(fn truncate_works() { let tmpdir = tmpdir(); let path = tmpdir.join("in.txt"); @@ -1200,7 +1175,7 @@ mod test { drop(file); }) - test!(fn open_flavors() { + iotest!(fn open_flavors() { let tmpdir = tmpdir(); match io::result(|| File::open_mode(&tmpdir.join("a"), io::Open, diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 0852c4cadb6..8481de73c7f 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -313,6 +313,10 @@ pub use self::net::udp::UdpStream; pub use self::pipe::PipeStream; pub use self::process::Process; +/// Testing helpers +#[cfg(test)] +mod test; + /// Synchronous, non-blocking filesystem operations. pub mod fs; diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index bd7d8bacb38..e7787692dd2 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -176,7 +176,7 @@ mod test { #[test] fn smoke_test_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -195,7 +195,7 @@ mod test { #[test] fn smoke_test_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -214,7 +214,7 @@ mod test { #[test] fn read_eof_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -233,7 +233,7 @@ mod test { #[test] fn read_eof_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -252,10 +252,10 @@ mod test { #[test] fn read_eof_twice_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { - port.take().recv(); + port.recv(); let _stream = TcpStream::connect(addr); // Close } @@ -281,7 +281,7 @@ mod test { #[test] fn read_eof_twice_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -310,7 +310,7 @@ mod test { #[test] fn write_close_ip4() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -342,7 +342,7 @@ mod test { #[test] fn write_close_ip6() { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -375,7 +375,7 @@ mod test { fn multiple_connect_serial_ip4() { let addr = next_test_ip4(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -398,7 +398,7 @@ mod test { fn multiple_connect_serial_ip6() { let addr = next_test_ip6(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -421,16 +421,15 @@ mod test { fn multiple_connect_interleaved_greedy_schedule_ip4() { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == i as u8); @@ -460,15 +459,15 @@ mod test { fn multiple_connect_interleaved_greedy_schedule_ip6() { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::<()>::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); + chan.send(()); for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == i as u8); @@ -498,16 +497,15 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip4() { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == 99); @@ -536,16 +534,15 @@ mod test { fn multiple_connect_interleaved_lazy_schedule_ip6() { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); chan.send(()); for stream in acceptor.incoming().take(MAX as uint) { - let stream = Cell::new(stream); // Start another task to handle the connection do spawn { - let mut stream = stream.take(); + let mut stream = stream; let mut buf = [0]; stream.read(buf); assert!(buf[0] == 99); @@ -573,23 +570,18 @@ mod test { #[cfg(test)] fn socket_name(addr: SocketAddr) { - do run_in_mt_newsched_task { - do spawntask { - let mut listener = TcpListener::bind(addr).unwrap(); - - // Make sure socket_name gives - // us the socket we binded to. - let so_name = listener.socket_name(); - assert!(so_name.is_some()); - assert_eq!(addr, so_name.unwrap()); + let mut listener = TcpListener::bind(addr).unwrap(); - } - } + // Make sure socket_name gives + // us the socket we binded to. + let so_name = listener.socket_name(); + assert!(so_name.is_some()); + assert_eq!(addr, so_name.unwrap()); } #[cfg(test)] fn peer_name(addr: SocketAddr) { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut acceptor = TcpListener::bind(addr).listen(); diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 159823ba2b5..7cb8f741cf3 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -101,6 +101,7 @@ mod test { use super::*; use io::net::ip::{Ipv4Addr, SocketAddr}; use io::*; + use io::test::*; use prelude::*; #[test] #[ignore] @@ -121,7 +122,7 @@ mod test { fn socket_smoke_test_ip4() { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -154,7 +155,7 @@ mod test { fn socket_smoke_test_ip6() { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::<()>::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -168,7 +169,7 @@ mod test { match UdpSocket::bind(server_ip) { Some(ref mut server) => { - chan.take().send(()); + chan.send(()); let mut buf = [0]; match server.recvfrom(buf) { Some((nread, src)) => { @@ -187,7 +188,7 @@ mod test { fn stream_smoke_test_ip4() { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { @@ -223,7 +224,7 @@ mod test { fn stream_smoke_test_ip6() { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { match UdpSocket::bind(client_ip) { diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 8fd256a22f9..59a6903adbf 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -141,11 +141,12 @@ mod tests { use prelude::*; use super::*; use io::*; + use io::test::*; fn smalltest(server: proc(UnixStream), client: proc(UnixStream)) { let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); @@ -229,7 +230,7 @@ mod tests { let times = 10; let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { port.recv(); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 88047aecda2..5249d331f72 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -308,23 +308,10 @@ impl Writer for StdWriter { #[cfg(test)] mod tests { - use super::*; - use rt::test::run_in_newsched_task; - - #[test] - fn smoke_uv() { + iotest!(fn smoke() { // Just make sure we can acquire handles stdin(); stdout(); stderr(); - } - - #[test] - fn smoke_native() { - do run_in_newsched_task { - stdin(); - stdout(); - stderr(); - } - } + }) } diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index 212e4ebffa8..dd24150e03e 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -8,9 +8,48 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#[macro_escape]; + +use os; +use prelude::*; +use rand; +use rand::Rng; +use std::io::net::ip::*; +use sync::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed}; + +macro_rules! iotest ( + { fn $name:ident() $b:block } => ( + mod $name { + #[allow(unused_imports)]; + + use super::super::*; + use super::*; + use io; + use prelude::*; + use io::*; + use io::fs::*; + use io::net::tcp::*; + use io::net::ip::*; + use io::net::udp::*; + use io::net::unix::*; + use str; + use util; + + fn f() $b + + #[test] fn green() { f() } + #[test] fn native() { + use native; + let (p, c) = Chan::new(); + do native::task::spawn { c.send(f()) } + p.recv(); + } + } + ) +) + /// Get a port number, starting at 9600, for use in tests pub fn next_test_port() -> u16 { - use unstable::atomics::{AtomicUint, INIT_ATOMIC_UINT, Relaxed}; static mut next_offset: AtomicUint = INIT_ATOMIC_UINT; unsafe { base_port() + next_offset.fetch_add(1, Relaxed) as u16 @@ -44,9 +83,6 @@ all want to use ports. This function figures out which workspace it is running in and assigns a port range based on it. */ fn base_port() -> u16 { - use os; - use str::StrSlice; - use vec::ImmutableVector; let base = 9600u16; let range = 1000u16; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 200e4e63261..4f633a63bab 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -65,13 +65,15 @@ // When testing libstd, bring in libuv as the I/O backend so tests can print // things and all of the std::io tests have an I/O interface to run on top // of -#[cfg(test)] extern mod rustuv = "rustuv#0.9-pre"; +#[cfg(test)] extern mod rustuv = "rustuv"; +#[cfg(test)] extern mod native = "native"; +#[cfg(test)] extern mod green = "green"; // Make extra accessible for benchmarking -#[cfg(test)] extern mod extra = "extra#0.9-pre"; +#[cfg(test)] extern mod extra = "extra"; // Make std testable by not duplicating lang items. See #2912 -#[cfg(test)] extern mod realstd = "std#0.9-pre"; +#[cfg(test)] extern mod realstd = "std"; #[cfg(test)] pub use kinds = realstd::kinds; #[cfg(test)] pub use ops = realstd::ops; #[cfg(test)] pub use cmp = realstd::cmp; diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index 652aa4d8198..d7e11d2f3a7 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -432,6 +432,7 @@ mod tests { } #[test] + #[allow(dead_code)] fn test_tls_overwrite_multiple_types() { static str_key: Key<~str> = &Key; static box_key: Key<@()> = &Key; diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index ea27956ad90..1c04b6b43ce 100644 --- a/src/libstd/rt/local.rs +++ b/src/libstd/rt/local.rs @@ -49,7 +49,6 @@ impl Local> for Task { mod test { use option::None; use unstable::run_in_bare_thread; - use rt::test::*; use super::*; use rt::task::Task; use rt::local_ptr; @@ -58,8 +57,7 @@ mod test { fn thread_local_task_smoke_test() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); @@ -70,12 +68,11 @@ mod test { fn thread_local_task_two_instances() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); let task: ~Task = Local::take(); cleanup_task(task); @@ -87,8 +84,7 @@ mod test { fn borrow_smoke_test() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); unsafe { @@ -103,8 +99,7 @@ mod test { fn borrow_with_return() { do run_in_bare_thread { local_ptr::init(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, None, proc(){}); + let task = ~Task::new(); Local::put(task); { @@ -116,5 +111,9 @@ mod test { } } + fn cleanup_task(mut t: ~Task) { + t.destroyed = true; + } + } diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 7602d7b0564..c0164891cd4 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -176,8 +176,12 @@ impl Task { // Cleanup the dynamic borrowck debugging info borrowck::clear_task_borrow_list(); + // TODO: dox + unsafe { + let me: *mut Task = Local::unsafe_borrow(); + (*me).death.collect_failure((*me).unwinder.result()); + } let mut me: ~Task = Local::take(); - me.death.collect_failure(me.unwinder.result()); me.destroyed = true; return me; } @@ -375,92 +379,76 @@ impl Drop for Death { #[cfg(test)] mod test { use super::*; - use rt::test::*; use prelude::*; + use task; #[test] fn local_heap() { - do run_in_newsched_task() { - let a = @5; - let b = a; - assert!(*a == 5); - assert!(*b == 5); - } + let a = @5; + let b = a; + assert!(*a == 5); + assert!(*b == 5); } #[test] fn tls() { use local_data; - do run_in_newsched_task() { - local_data_key!(key: @~str) - local_data::set(key, @~"data"); - assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data"); - local_data_key!(key2: @~str) - local_data::set(key2, @~"data"); - assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data"); - } + local_data_key!(key: @~str) + local_data::set(key, @~"data"); + assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data"); + local_data_key!(key2: @~str) + local_data::set(key2, @~"data"); + assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data"); } #[test] fn unwind() { - do run_in_newsched_task() { - let result = spawntask_try(proc()()); - rtdebug!("trying first assert"); - assert!(result.is_ok()); - let result = spawntask_try(proc() fail!()); - rtdebug!("trying second assert"); - assert!(result.is_err()); - } + let result = task::try(proc()()); + rtdebug!("trying first assert"); + assert!(result.is_ok()); + let result = task::try::<()>(proc() fail!()); + rtdebug!("trying second assert"); + assert!(result.is_err()); } #[test] fn rng() { - do run_in_uv_task() { - use rand::{rng, Rng}; - let mut r = rng(); - let _ = r.next_u32(); - } + use rand::{rng, Rng}; + let mut r = rng(); + let _ = r.next_u32(); } #[test] fn logging() { - do run_in_uv_task() { - info!("here i am. logging in a newsched task"); - } + info!("here i am. logging in a newsched task"); } #[test] fn comm_stream() { - do run_in_newsched_task() { - let (port, chan) = Chan::new(); - chan.send(10); - assert!(port.recv() == 10); - } + let (port, chan) = Chan::new(); + chan.send(10); + assert!(port.recv() == 10); } #[test] fn comm_shared_chan() { - do run_in_newsched_task() { - let (port, chan) = SharedChan::new(); - chan.send(10); - assert!(port.recv() == 10); - } + let (port, chan) = SharedChan::new(); + chan.send(10); + assert!(port.recv() == 10); } #[test] fn heap_cycles() { use option::{Option, Some, None}; - do run_in_newsched_task { - struct List { - next: Option<@mut List>, - } + struct List { + next: Option<@mut List>, + } - let a = @mut List { next: None }; - let b = @mut List { next: Some(a) }; + let a = @mut List { next: None }; + let b = @mut List { next: Some(a) }; - a.next = Some(b); - } + a.next = Some(b); } #[test] @@ -471,8 +459,8 @@ mod test { #[test] fn block_and_wake() { - do with_test_task |task| { - BlockedTask::block(task).wake().unwrap() - } + let task = ~Task::new(); + let mut task = BlockedTask::block(task).wake().unwrap(); + task.destroyed = true; } } diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 15c0986f899..69704c855ee 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -426,13 +426,13 @@ mod tests { } fn writeclose(fd: c_int, s: &str) { - let mut writer = PipeStream::open(fd as int); + let mut writer = PipeStream::open(fd); writer.write(s.as_bytes()); } fn readclose(fd: c_int) -> ~str { let mut res = ~[]; - let mut reader = PipeStream::open(fd as int); + let mut reader = PipeStream::open(fd); let mut buf = [0, ..1024]; loop { match reader.read(buf) { diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index 7632ec6cf29..7b94a3acc2b 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -131,7 +131,6 @@ impl Drop for UnsafeArc{ mod tests { use prelude::*; use super::UnsafeArc; - use task; use mem::size_of; #[test] diff --git a/src/libstd/sync/mpmc_bounded_queue.rs b/src/libstd/sync/mpmc_bounded_queue.rs index b623976306d..fe51de4e42d 100644 --- a/src/libstd/sync/mpmc_bounded_queue.rs +++ b/src/libstd/sync/mpmc_bounded_queue.rs @@ -163,8 +163,8 @@ impl Clone for Queue { mod tests { use prelude::*; use option::*; - use task; use super::Queue; + use native; #[test] fn test() { @@ -172,14 +172,17 @@ mod tests { let nmsgs = 1000u; let mut q = Queue::with_capacity(nthreads*nmsgs); assert_eq!(None, q.pop()); + let (port, chan) = SharedChan::new(); for _ in range(0, nthreads) { let q = q.clone(); - do task::spawn_sched(task::SingleThreaded) { + let chan = chan.clone(); + do native::task::spawn { let mut q = q; for i in range(0, nmsgs) { assert!(q.push(i)); } + chan.send(()); } } @@ -188,7 +191,7 @@ mod tests { let (completion_port, completion_chan) = Chan::new(); completion_ports.push(completion_port); let q = q.clone(); - do task::spawn_sched(task::SingleThreaded) { + do native::task::spawn { let mut q = q; let mut i = 0u; loop { @@ -207,5 +210,8 @@ mod tests { for completion_port in completion_ports.mut_iter() { assert_eq!(nmsgs, completion_port.recv()); } + for _ in range(0, nthreads) { + port.recv(); + } } } diff --git a/src/libstd/sync/mpsc_queue.rs b/src/libstd/sync/mpsc_queue.rs index 89e56e3fa67..a249d6ed2e8 100644 --- a/src/libstd/sync/mpsc_queue.rs +++ b/src/libstd/sync/mpsc_queue.rs @@ -203,8 +203,8 @@ impl Consumer { mod tests { use prelude::*; - use task; use super::{queue, Data, Empty, Inconsistent}; + use native; #[test] fn test_full() { @@ -222,14 +222,17 @@ mod tests { Empty => {} Inconsistent | Data(..) => fail!() } + let (port, chan) = SharedChan::new(); for _ in range(0, nthreads) { let q = p.clone(); - do task::spawn_sched(task::SingleThreaded) { + let chan = chan.clone(); + do native::task::spawn { let mut q = q; for i in range(0, nmsgs) { q.push(i); } + chan.send(()); } } @@ -240,6 +243,9 @@ mod tests { Data(_) => { i += 1 } } } + for _ in range(0, nthreads) { + port.recv(); + } } } diff --git a/src/libstd/sync/spsc_queue.rs b/src/libstd/sync/spsc_queue.rs index c4abba04659..6f1b887c271 100644 --- a/src/libstd/sync/spsc_queue.rs +++ b/src/libstd/sync/spsc_queue.rs @@ -268,7 +268,7 @@ impl Drop for State { mod test { use prelude::*; use super::queue; - use task; + use native; #[test] fn smoke() { @@ -314,7 +314,8 @@ mod test { fn stress_bound(bound: uint) { let (c, mut p) = queue(bound, ()); - do task::spawn_sched(task::SingleThreaded) { + let (port, chan) = Chan::new(); + do native::task::spawn { let mut c = c; for _ in range(0, 100000) { loop { @@ -325,10 +326,12 @@ mod test { } } } + chan.send(()); } for _ in range(0, 100000) { p.push(1); } + port.recv(); } } } diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 4632a3cf6e0..3b9cde5f44d 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -64,6 +64,7 @@ use send_str::{SendStr, IntoSendStr}; use str::Str; use util; +#[cfg(test)] use any::{AnyOwnExt, AnyRefExt}; #[cfg(test)] use comm::SharedChan; #[cfg(test)] use ptr; #[cfg(test)] use result; @@ -385,59 +386,43 @@ pub fn failing() -> bool { #[test] fn test_unnamed_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - do spawn { - with_task_name(|name| { - assert!(name.is_none()); - }) - } + do spawn { + with_task_name(|name| { + assert!(name.is_none()); + }) } } #[test] fn test_owned_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name(~"ada lovelace"); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name(~"ada lovelace"); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } #[test] fn test_static_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name("ada lovelace"); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name("ada lovelace"); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } #[test] fn test_send_named_task() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - let mut t = task(); - t.name("ada lovelace".into_send_str()); - do t.spawn { - with_task_name(|name| { - assert!(name.unwrap() == "ada lovelace"); - }) - } + let mut t = task(); + t.name("ada lovelace".into_send_str()); + do t.spawn { + with_task_name(|name| { + assert!(name.unwrap() == "ada lovelace"); + }) } } @@ -508,28 +493,19 @@ fn test_try_fail() { } } -#[cfg(test)] -fn get_sched_id() -> int { - use rt::sched::Scheduler; - let mut sched = Local::borrow(None::); - sched.get().sched_id() as int -} - #[test] fn test_spawn_sched() { + use clone::Clone; + let (po, ch) = SharedChan::new(); fn f(i: int, ch: SharedChan<()>) { - let parent_sched_id = get_sched_id(); - - do spawn_sched(SingleThreaded) { - let child_sched_id = get_sched_id(); - assert!(parent_sched_id != child_sched_id); - + let ch = ch.clone(); + do spawn { if (i == 0) { ch.send(()); } else { - f(i - 1, ch.clone()); + f(i - 1, ch); } }; @@ -542,16 +518,9 @@ fn test_spawn_sched() { fn test_spawn_sched_childs_on_default_sched() { let (po, ch) = Chan::new(); - // Assuming tests run on the default scheduler - let default_id = get_sched_id(); - - do spawn_sched(SingleThreaded) { + do spawn { let ch = ch; - let parent_sched_id = get_sched_id(); do spawn { - let child_sched_id = get_sched_id(); - assert!(parent_sched_id != child_sched_id); - assert_eq!(child_sched_id, default_id); ch.send(()); }; }; @@ -562,6 +531,7 @@ fn test_spawn_sched_childs_on_default_sched() { #[test] fn test_spawn_sched_blocking() { use unstable::mutex::Mutex; + use num::Times; unsafe { @@ -574,7 +544,7 @@ fn test_spawn_sched_blocking() { let mut lock = Mutex::new(); let lock2 = lock.clone(); - do spawn_sched(SingleThreaded) { + do spawn { let mut lock = lock2; lock.lock(); @@ -681,11 +651,7 @@ fn test_child_doesnt_ref_parent() { #[test] fn test_simple_newsched_spawn() { - use rt::test::run_in_uv_task; - - do run_in_uv_task { - spawn(proc()()) - } + spawn(proc()()) } #[test] diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs index eaf716f2726..5b2fac8e74e 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/libstd/unstable/mutex.rs @@ -333,12 +333,12 @@ mod test { fn somke_cond() { static mut lock: Mutex = MUTEX_INIT; unsafe { + lock.lock(); let t = do Thread::start { lock.lock(); lock.signal(); lock.unlock(); }; - lock.lock(); lock.wait(); lock.unlock(); t.join(); diff --git a/src/libstd/unstable/stack.rs b/src/libstd/unstable/stack.rs index 46a3a80be25..b8788b8c55c 100644 --- a/src/libstd/unstable/stack.rs +++ b/src/libstd/unstable/stack.rs @@ -24,11 +24,6 @@ //! detection is not guaranteed to continue in the future. Usage of this module //! is discouraged unless absolutely necessary. -use rt::task::Task; -use option::None; -use rt::local::Local; -use unstable::intrinsics; - static RED_ZONE: uint = 20 * 1024; /// This function is invoked from rust's current __morestack function. Segmented @@ -41,6 +36,10 @@ static RED_ZONE: uint = 20 * 1024; // irrelevant for documentation purposes. #[cfg(not(test))] // in testing, use the original libstd's version pub extern "C" fn rust_stack_exhausted() { + use rt::task::Task; + use option::None; + use rt::local::Local; + use unstable::intrinsics; unsafe { // We're calling this function because the stack just ran out. We need diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index ad36f71cdea..687efea939b 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -161,9 +161,8 @@ impl Exclusive { mod tests { use option::*; use prelude::*; - use super::{Exclusive, UnsafeArc, atomic}; + use super::Exclusive; use task; - use mem::size_of; #[test] fn exclusive_new_arc() { diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index 97d4c2f6d1b..86f28c28f69 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -2874,7 +2874,6 @@ impl Extendable for ~[A] { #[cfg(test)] mod tests { - use option::{None, Some}; use mem; use vec::*; use cmp::*; -- cgit 1.4.1-3-g733a5 From 282f3d99a5ad85acbc58c03b5dfcdabf649c0c85 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Dec 2013 21:14:08 -0800 Subject: Test fixes and rebase problems Note that this removes a number of run-pass tests which are exercising behavior of the old runtime. This functionality no longer exists and is thoroughly tested inside of libgreen and libnative. There isn't really the notion of "starting the runtime" any more. The major notion now is "bootstrapping the initial task". --- Makefile.in | 6 +++ mk/target.mk | 7 +-- src/compiletest/compiletest.rs | 4 +- src/etc/licenseck.py | 6 +-- src/libgreen/lib.rs | 22 ++++++--- src/libnative/lib.rs | 2 + src/librustuv/homing.rs | 2 + src/librustuv/queue.rs | 2 + src/libstd/io/test.rs | 79 +++++++++++++++++++++++++++++++ src/libstd/rt/task.rs | 18 ++++++- src/libstd/sync/arc.rs | 2 +- src/libsyntax/ext/build.rs | 1 - src/test/run-pass/core-rt-smoke.rs | 20 -------- src/test/run-pass/native-print-no-uv.rs | 17 ------- src/test/run-pass/rt-run-twice.rs | 26 ---------- src/test/run-pass/rt-start-main-thread.rs | 21 -------- src/test/run-pass/spawning-with-debug.rs | 1 - src/test/run-pass/use.rs | 5 +- 18 files changed, 134 insertions(+), 107 deletions(-) delete mode 100644 src/test/run-pass/core-rt-smoke.rs delete mode 100644 src/test/run-pass/native-print-no-uv.rs delete mode 100644 src/test/run-pass/rt-run-twice.rs delete mode 100644 src/test/run-pass/rt-start-main-thread.rs (limited to 'src/libstd/sync') diff --git a/Makefile.in b/Makefile.in index d5a62f11e05..f1b18e8f64b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -280,9 +280,15 @@ define CHECK_FOR_OLD_GLOB_MATCHES_EXCEPT endef # Same interface as above, but deletes rather than just listing the files. +ifdef VERBOSE define REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT $(Q)MATCHES="$(filter-out %$(3),$(wildcard $(1)/$(2)))"; if [ -n "$$MATCHES" ] ; then echo "warning: removing previous" \'$(2)\' "libraries:" $$MATCHES; rm $$MATCHES ; fi endef +else +define REMOVE_ALL_OLD_GLOB_MATCHES_EXCEPT + $(Q)MATCHES="$(filter-out %$(3),$(wildcard $(1)/$(2)))"; if [ -n "$$MATCHES" ] ; then rm $$MATCHES ; fi +endef +endif # We use a different strategy for LIST_ALL_OLD_GLOB_MATCHES_EXCEPT # than in the macros above because it needs the result of running the diff --git a/mk/target.mk b/mk/target.mk index 3746a4eafc0..db8488f792f 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -161,16 +161,13 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(3)): \ $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_GLOB_$(2)),$$(notdir $$@)) $$(call LIST_ALL_OLD_GLOB_MATCHES_EXCEPT,$$(dir $$@),$(LIBRUSTC_RGLOB_$(2)),$$(notdir $$@)) -# NOTE: after the next snapshot remove these '-L' flags $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(3)): \ $$(DRIVER_CRATE) \ - $$(TSREQ$(1)_T_$(2)_H_$(3)) \ + $$(SREQ$(1)_T_$(2)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$(CFG_LIBRUSTC_$(3)) \ | $$(TBIN$(1)_T_$(2)_H_$(3))/ @$$(call E, compile_and_link: $$@) - $$(STAGE$(1)_T_$(2)_H_$(3)) --cfg rustc -o $$@ $$< \ - -L $$(UV_SUPPORT_DIR_$(2)) \ - -L $$(dir $$(LIBUV_LIB_$(2))) + $$(STAGE$(1)_T_$(2)_H_$(3)) --cfg rustc -o $$@ $$< ifdef CFG_ENABLE_PAX_FLAGS @$$(call E, apply PaX flags: $$@) @"$(CFG_PAXCTL)" -cm "$$@" diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 0fb75b7c8e0..89b6f06abfc 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -16,7 +16,7 @@ extern mod extra; use std::os; -use std::rt; +use std::io; use std::io::fs; use extra::getopts; @@ -234,7 +234,7 @@ pub fn run_tests(config: &config) { // sadly osx needs some file descriptor limits raised for running tests in // parallel (especially when we have lots and lots of child processes). // For context, see #8904 - rt::test::prepare_for_lots_of_tests(); + io::test::raise_fd_limit(); let res = test::run_tests_console(&opts, tests); if !res { fail!("Some tests failed"); } } diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 78d0973fdfe..073322b0815 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -76,9 +76,9 @@ exceptions = [ "rt/isaac/randport.cpp", # public domain "rt/isaac/rand.h", # public domain "rt/isaac/standard.h", # public domain - "libstd/rt/mpsc_queue.rs", # BSD - "libstd/rt/spsc_queue.rs", # BSD - "libstd/rt/mpmc_bounded_queue.rs", # BSD + "libstd/sync/mpsc_queue.rs", # BSD + "libstd/sync/spsc_queue.rs", # BSD + "libstd/sync/mpmc_bounded_queue.rs", # BSD ] def check_license(name, contents): diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index bb219936ae0..57e2a0bfe16 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -17,6 +17,7 @@ //! This can be optionally linked in to rust programs in order to provide M:N //! functionality inside of 1:1 programs. +#[pkgid = "green#0.9-pre"]; #[link(name = "green", package_id = "green", vers = "0.9-pre", @@ -30,17 +31,16 @@ // NB this does *not* include globs, please keep it that way. #[feature(macro_rules)]; -use std::cast; use std::os; -use std::rt::thread::Thread; -use std::rt; use std::rt::crate_map; use std::rt::rtio; -use std::sync::deque; +use std::rt::thread::Thread; +use std::rt; use std::sync::atomics::{SeqCst, AtomicUint, INIT_ATOMIC_UINT}; +use std::sync::deque; use std::task::TaskOpts; -use std::vec; use std::util; +use std::vec; use stdtask = std::rt::task; use sched::{Shutdown, Scheduler, SchedHandle, TaskFromFriend, NewNeighbor}; @@ -58,9 +58,9 @@ pub mod sleeper_list; pub mod stack; pub mod task; -#[cfg(stage0)] #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { + use std::cast; do start(argc, argv) { let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; main(); @@ -103,7 +103,15 @@ pub fn start(argc: int, argv: **u8, main: proc()) -> int { /// have returned. pub fn run(main: proc()) -> int { let mut pool = SchedPool::new(PoolConfig::new()); - pool.spawn(TaskOpts::new(), main); + let (port, chan) = Chan::new(); + let mut opts = TaskOpts::new(); + opts.notify_chan = Some(chan); + pool.spawn(opts, main); + do pool.spawn(TaskOpts::new()) { + if port.recv().is_err() { + os::set_exit_status(rt::DEFAULT_ERROR_CODE); + } + } unsafe { stdtask::wait_for_completion(); } pool.shutdown(); os::get_exit_status() diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index b97d9127277..44b66a7804d 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -14,6 +14,7 @@ //! runtime. In addition, all I/O provided by this crate is the thread blocking //! version of I/O. +#[pkgid = "native#0.9-pre"]; #[link(name = "native", package_id = "native", vers = "0.9-pre", @@ -24,6 +25,7 @@ #[crate_type = "rlib"]; #[crate_type = "dylib"]; +// Allow check-stage0-native for now #[cfg(stage0, test)] extern mod green; // NB this crate explicitly does *not* allow glob imports, please seriously diff --git a/src/librustuv/homing.rs b/src/librustuv/homing.rs index 1ee64398ca3..d7be06724a0 100644 --- a/src/librustuv/homing.rs +++ b/src/librustuv/homing.rs @@ -31,6 +31,8 @@ //! This enqueueing is done with a concurrent queue from libstd, and the //! signalling is achieved with an async handle. +#[allow(dead_code)]; + use std::rt::local::Local; use std::rt::rtio::LocalIo; use std::rt::task::{Task, BlockedTask}; diff --git a/src/librustuv/queue.rs b/src/librustuv/queue.rs index 22e7925b211..b36bdf62775 100644 --- a/src/librustuv/queue.rs +++ b/src/librustuv/queue.rs @@ -18,6 +18,8 @@ //! event loop alive we use uv_ref and uv_unref in order to control when the //! async handle is active or not. +#[allow(dead_code)]; + use std::cast; use std::libc::{c_void, c_int}; use std::rt::task::BlockedTask; diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index dd24150e03e..e273aedf7cc 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -113,3 +113,82 @@ fn base_port() -> u16 { return final_base; } + +pub fn raise_fd_limit() { + unsafe { darwin_fd_limit::raise_fd_limit() } +} + +#[cfg(target_os="macos")] +#[allow(non_camel_case_types)] +mod darwin_fd_limit { + /*! + * darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the + * rlimit maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low + * for our multithreaded scheduler testing, depending on the number of cores available. + * + * This fixes issue #7772. + */ + + use libc; + type rlim_t = libc::uint64_t; + struct rlimit { + rlim_cur: rlim_t, + rlim_max: rlim_t + } + #[nolink] + extern { + // name probably doesn't need to be mut, but the C function doesn't specify const + fn sysctl(name: *mut libc::c_int, namelen: libc::c_uint, + oldp: *mut libc::c_void, oldlenp: *mut libc::size_t, + newp: *mut libc::c_void, newlen: libc::size_t) -> libc::c_int; + fn getrlimit(resource: libc::c_int, rlp: *mut rlimit) -> libc::c_int; + fn setrlimit(resource: libc::c_int, rlp: *rlimit) -> libc::c_int; + } + static CTL_KERN: libc::c_int = 1; + static KERN_MAXFILESPERPROC: libc::c_int = 29; + static RLIMIT_NOFILE: libc::c_int = 8; + + pub unsafe fn raise_fd_limit() { + // The strategy here is to fetch the current resource limits, read the kern.maxfilesperproc + // sysctl value, and bump the soft resource limit for maxfiles up to the sysctl value. + use ptr::{to_unsafe_ptr, to_mut_unsafe_ptr, mut_null}; + use mem::size_of_val; + use os::last_os_error; + + // Fetch the kern.maxfilesperproc value + let mut mib: [libc::c_int, ..2] = [CTL_KERN, KERN_MAXFILESPERPROC]; + let mut maxfiles: libc::c_int = 0; + let mut size: libc::size_t = size_of_val(&maxfiles) as libc::size_t; + if sysctl(to_mut_unsafe_ptr(&mut mib[0]), 2, + to_mut_unsafe_ptr(&mut maxfiles) as *mut libc::c_void, + to_mut_unsafe_ptr(&mut size), + mut_null(), 0) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling sysctl: {}", err); + return; + } + + // Fetch the current resource limits + let mut rlim = rlimit{rlim_cur: 0, rlim_max: 0}; + if getrlimit(RLIMIT_NOFILE, to_mut_unsafe_ptr(&mut rlim)) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling getrlimit: {}", err); + return; + } + + // Bump the soft limit to the smaller of kern.maxfilesperproc and the hard limit + rlim.rlim_cur = ::cmp::min(maxfiles as rlim_t, rlim.rlim_max); + + // Set our newly-increased resource limit + if setrlimit(RLIMIT_NOFILE, to_unsafe_ptr(&rlim)) != 0 { + let err = last_os_error(); + error!("raise_fd_limit: error calling setrlimit: {}", err); + return; + } + } +} + +#[cfg(not(target_os="macos"))] +mod darwin_fd_limit { + pub unsafe fn raise_fd_limit() {} +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 91e285b1061..c0e1086483d 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -38,8 +38,13 @@ use task::{TaskResult, TaskOpts}; use unstable::finally::Finally; use unstable::mutex::{Mutex, MUTEX_INIT}; -#[cfg(stage0)] pub use rt::unwind::begin_unwind; +#[cfg(stage0)] +pub use rt::unwind::begin_unwind; +// These two statics are used as bookeeping to keep track of the rust runtime's +// count of threads. In 1:1 contexts, this is used to know when to return from +// the main function, and in M:N contexts this is used to know when to shut down +// the pool of schedulers. static mut TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; static mut TASK_LOCK: Mutex = MUTEX_INIT; @@ -181,10 +186,15 @@ impl Task { // Cleanup the dynamic borrowck debugging info borrowck::clear_task_borrow_list(); - // TODO: dox + // Here we must unsafely borrow the task in order to not remove it from + // TLS. When collecting failure, we may attempt to send on a channel (or + // just run aribitrary code), so we must be sure to still have a local + // task in TLS. unsafe { let me: *mut Task = Local::unsafe_borrow(); (*me).death.collect_failure((*me).unwinder.result()); + + // see comments on these statics for why they're used if TASK_COUNT.fetch_sub(1, SeqCst) == 1 { TASK_LOCK.lock(); TASK_LOCK.signal(); @@ -386,6 +396,10 @@ impl Drop for Death { } } +/// The main function of all rust executables will by default use this function. +/// This function will *block* the OS thread (hence the `unsafe`) waiting for +/// all known tasks to complete. Once this function has returned, it is +/// guaranteed that no more user-defined code is still running. pub unsafe fn wait_for_completion() { TASK_LOCK.lock(); while TASK_COUNT.load(SeqCst) > 0 { diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index 7b94a3acc2b..b405104c09a 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -32,7 +32,7 @@ use vec; /// An atomically reference counted pointer. /// /// Enforces no shared-memory safety. -#[unsafe_no_drop_flag] +//#[unsafe_no_drop_flag] FIXME: #9758 pub struct UnsafeArc { priv data: *mut ArcData, } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 930d25e7443..aa7e0d0eced 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -606,7 +606,6 @@ impl AstBuilder for @ExtCtxt { ~[ self.ident_of("std"), self.ident_of("rt"), - self.ident_of("task"), self.ident_of("begin_unwind"), ], ~[ diff --git a/src/test/run-pass/core-rt-smoke.rs b/src/test/run-pass/core-rt-smoke.rs deleted file mode 100644 index 6e3d9629da0..00000000000 --- a/src/test/run-pass/core-rt-smoke.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast - -// A simple test of starting the runtime manually - -#[start] -fn start(argc: int, argv: **u8) -> int { - do std::rt::start(argc, argv) { - info!("creating my own runtime is joy"); - } -} diff --git a/src/test/run-pass/native-print-no-uv.rs b/src/test/run-pass/native-print-no-uv.rs deleted file mode 100644 index d3b6d605984..00000000000 --- a/src/test/run-pass/native-print-no-uv.rs +++ /dev/null @@ -1,17 +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. - -// xfail-fast - -#[no_uv]; - -fn main() { - println!("hello"); -} diff --git a/src/test/run-pass/rt-run-twice.rs b/src/test/run-pass/rt-run-twice.rs deleted file mode 100644 index a9a26c2fbb1..00000000000 --- a/src/test/run-pass/rt-run-twice.rs +++ /dev/null @@ -1,26 +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. - -// xfail-fast make-check does not like `#[start]` - -use std::rt; - -#[start] -fn start(argc: int, argv: **u8) -> int { - do rt::start(argc, argv) { - println("First invocation"); - }; - - do rt::start(argc, argv) { - println("Second invocation"); - }; - - 0 -} diff --git a/src/test/run-pass/rt-start-main-thread.rs b/src/test/run-pass/rt-start-main-thread.rs deleted file mode 100644 index 47a723ce6e1..00000000000 --- a/src/test/run-pass/rt-start-main-thread.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. - -// xfail-fast - -#[start] -fn start(argc: int, argv: **u8) -> int { - do std::rt::start_on_main_thread(argc, argv) { - info!("running on main thread"); - do spawn { - info!("running on another thread"); - } - } -} diff --git a/src/test/run-pass/spawning-with-debug.rs b/src/test/run-pass/spawning-with-debug.rs index 76975d15c1d..f8094f9fdb9 100644 --- a/src/test/run-pass/spawning-with-debug.rs +++ b/src/test/run-pass/spawning-with-debug.rs @@ -17,6 +17,5 @@ use std::task; fn main() { let mut t = task::task(); - t.sched_mode(task::SingleThreaded); t.spawn(proc() ()); } diff --git a/src/test/run-pass/use.rs b/src/test/run-pass/use.rs index ddd4b10fd5c..56ce5397efb 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -10,6 +10,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-fast + #[allow(unused_imports)]; #[no_std]; @@ -25,4 +27,5 @@ mod baz { pub use x = std::str; } -pub fn main() { } +#[start] +pub fn start(_: int, _: **u8) -> int { 3 } -- cgit 1.4.1-3-g733a5 From 6cad8f4f14da1dd529100779db74b03d6db20faf Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 18 Dec 2013 09:57:58 -0800 Subject: Test fixes and rebase conflicts * vec::raw::to_ptr is gone * Pausible => Pausable * Removing @ * Calling the main task "
" * Removing unused imports * Removing unused mut * Bringing some libextra tests up to date * Allowing compiletest to work at stage0 * Fixing the bootstrap-from-c rmake tests * assert => rtassert in a few cases * printing to stderr instead of stdout in fail!() --- src/compiletest/compiletest.rs | 1 + src/libextra/comm.rs | 11 +- src/libextra/sync.rs | 32 ++-- src/libextra/task_pool.rs | 2 - src/libgreen/basic.rs | 2 +- src/libgreen/lib.rs | 29 ++- src/libgreen/macros.rs | 3 +- src/libgreen/sched.rs | 7 +- src/libnative/io/process.rs | 3 +- src/libnative/lib.rs | 13 +- src/librustc/back/link.rs | 12 +- src/librustpkg/tests.rs | 9 +- src/librustuv/file.rs | 1 - src/librustuv/idle.rs | 2 +- src/librustuv/macros.rs | 3 +- src/librustuv/signal.rs | 2 +- src/librustuv/timer.rs | 2 +- src/librustuv/uvio.rs | 6 +- src/libstd/io/net/unix.rs | 3 +- src/libstd/io/stdio.rs | 3 +- src/libstd/io/test.rs | 1 + src/libstd/rt/local_ptr.rs | 6 +- src/libstd/rt/mod.rs | 1 + src/libstd/rt/rtio.rs | 18 +- src/libstd/rt/task.rs | 8 +- src/libstd/rt/thread.rs | 4 - src/libstd/rt/unwind.rs | 202 ++++++++++++--------- src/libstd/rt/util.rs | 3 +- src/libstd/sync/arc.rs | 2 +- src/libstd/unstable/stack.rs | 2 + src/test/bench/rt-messaging-ping-pong.rs | 6 +- src/test/bench/rt-parfib.rs | 3 +- src/test/bench/shootout-spectralnorm.rs | 2 + src/test/compile-fail/std-uncopyable-atomics.rs | 2 +- .../run-make/bootstrap-from-c-with-green/Makefile | 9 + .../run-make/bootstrap-from-c-with-green/lib.rs | 25 +++ .../run-make/bootstrap-from-c-with-green/main.c | 16 ++ .../run-make/bootstrap-from-c-with-native/Makefile | 9 + .../run-make/bootstrap-from-c-with-native/lib.rs | 24 +++ .../run-make/bootstrap-from-c-with-native/main.c | 16 ++ .../run-make/bootstrap-from-c-with-uvio/Makefile | 9 - .../run-make/bootstrap-from-c-with-uvio/lib.rs | 25 --- .../run-make/bootstrap-from-c-with-uvio/main.c | 16 -- src/test/run-pass/use.rs | 2 +- 44 files changed, 320 insertions(+), 237 deletions(-) create mode 100644 src/test/run-make/bootstrap-from-c-with-green/Makefile create mode 100644 src/test/run-make/bootstrap-from-c-with-green/lib.rs create mode 100644 src/test/run-make/bootstrap-from-c-with-green/main.c create mode 100644 src/test/run-make/bootstrap-from-c-with-native/Makefile create mode 100644 src/test/run-make/bootstrap-from-c-with-native/lib.rs create mode 100644 src/test/run-make/bootstrap-from-c-with-native/main.c delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/Makefile delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/lib.rs delete mode 100644 src/test/run-make/bootstrap-from-c-with-uvio/main.c (limited to 'src/libstd/sync') diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 89b6f06abfc..ae7d1a30a84 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -13,6 +13,7 @@ #[allow(non_camel_case_types)]; #[deny(warnings)]; +#[cfg(stage0)] extern mod green; extern mod extra; use std::os; diff --git a/src/libextra/comm.rs b/src/libextra/comm.rs index c3b17fe9964..52b5bedb7ea 100644 --- a/src/libextra/comm.rs +++ b/src/libextra/comm.rs @@ -96,7 +96,6 @@ pub fn rendezvous() -> (SyncPort, SyncChan) { #[cfg(test)] mod test { use comm::{DuplexStream, rendezvous}; - use std::rt::test::run_in_uv_task; #[test] @@ -124,13 +123,11 @@ mod test { #[test] fn recv_a_lot() { // Rendezvous streams should be able to handle any number of messages being sent - do run_in_uv_task { - let (port, chan) = rendezvous(); - do spawn { - 1000000.times(|| { chan.send(()) }) - } - 1000000.times(|| { port.recv() }) + let (port, chan) = rendezvous(); + do spawn { + 1000000.times(|| { chan.send(()) }) } + 1000000.times(|| { port.recv() }) } #[test] diff --git a/src/libextra/sync.rs b/src/libextra/sync.rs index 2a53775a907..f43329076c8 100644 --- a/src/libextra/sync.rs +++ b/src/libextra/sync.rs @@ -761,23 +761,21 @@ mod tests { fn test_sem_runtime_friendly_blocking() { // Force the runtime to schedule two threads on the same sched_loop. // When one blocks, it should schedule the other one. - do task::spawn_sched(task::SingleThreaded) { - let s = Semaphore::new(1); - let s2 = s.clone(); - let (p, c) = Chan::new(); - let mut child_data = Some((s2, c)); - s.access(|| { - let (s2, c) = child_data.take_unwrap(); - do task::spawn { - c.send(()); - s2.access(|| { }); - c.send(()); - } - let _ = p.recv(); // wait for child to come alive - 5.times(|| { task::deschedule(); }); // let the child contend - }); - let _ = p.recv(); // wait for child to be done - } + let s = Semaphore::new(1); + let s2 = s.clone(); + let (p, c) = Chan::new(); + let mut child_data = Some((s2, c)); + s.access(|| { + let (s2, c) = child_data.take_unwrap(); + do task::spawn { + c.send(()); + s2.access(|| { }); + c.send(()); + } + let _ = p.recv(); // wait for child to come alive + 5.times(|| { task::deschedule(); }); // let the child contend + }); + let _ = p.recv(); // wait for child to be done } /************************************************************************ * Mutex tests diff --git a/src/libextra/task_pool.rs b/src/libextra/task_pool.rs index 649a9a06644..ba38f876287 100644 --- a/src/libextra/task_pool.rs +++ b/src/libextra/task_pool.rs @@ -17,8 +17,6 @@ use std::task; use std::vec; -#[cfg(test)] use std::task::SingleThreaded; - enum Msg { Execute(proc(&T)), Quit diff --git a/src/libgreen/basic.rs b/src/libgreen/basic.rs index e1e489a2a2b..0574792c18d 100644 --- a/src/libgreen/basic.rs +++ b/src/libgreen/basic.rs @@ -16,7 +16,7 @@ //! loop if no other one is provided (and M:N scheduling is desired). use std::cast; -use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback, +use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausableIdleCallback, Callback}; use std::unstable::sync::Exclusive; use std::util; diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index 7318eaaf679..3a2e8a2b36c 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -18,12 +18,7 @@ //! functionality inside of 1:1 programs. #[pkgid = "green#0.9-pre"]; -#[link(name = "green", - package_id = "green", - vers = "0.9-pre", - uuid = "20c38f8c-bfea-83ed-a068-9dc05277be26", - url = "https://github.com/mozilla/rust/tree/master/src/libgreen")]; - +#[crate_id = "green#0.9-pre"]; #[license = "MIT/ASL2"]; #[crate_type = "rlib"]; #[crate_type = "dylib"]; @@ -61,16 +56,13 @@ pub mod stack; pub mod task; #[lang = "start"] +#[cfg(not(test))] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; - let mut ret = None; - simple::task().run(|| { - ret = Some(do start(argc, argv) { - let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; - main(); - }) - }); - ret.unwrap() + do start(argc, argv) { + let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; + main(); + } } /// Set up a default runtime configuration, given compiler-supplied arguments. @@ -93,10 +85,14 @@ pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { /// error. pub fn start(argc: int, argv: **u8, main: proc()) -> int { rt::init(argc, argv); - let exit_code = run(main); + let mut main = Some(main); + let mut ret = None; + simple::task().run(|| { + ret = Some(run(main.take_unwrap())); + }); // unsafe is ok b/c we're sure that the runtime is gone unsafe { rt::cleanup() } - exit_code + ret.unwrap() } /// Execute the main function in a pool of M:N schedulers. @@ -114,6 +110,7 @@ pub fn run(main: proc()) -> int { let (port, chan) = Chan::new(); let mut opts = TaskOpts::new(); opts.notify_chan = Some(chan); + opts.name = Some(SendStrStatic("
")); pool.spawn(opts, main); // Wait for the main task to return, and set the process error code diff --git a/src/libgreen/macros.rs b/src/libgreen/macros.rs index ad0854e2b1e..56dc3204da8 100644 --- a/src/libgreen/macros.rs +++ b/src/libgreen/macros.rs @@ -54,14 +54,13 @@ macro_rules! rtabort ( pub fn dumb_println(args: &fmt::Arguments) { use std::io; use std::libc; - use std::vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libgreen/sched.rs b/src/libgreen/sched.rs index 95c4d8347d5..ef62f654ddf 100644 --- a/src/libgreen/sched.rs +++ b/src/libgreen/sched.rs @@ -11,7 +11,7 @@ use std::cast; use std::rand::{XorShiftRng, Rng, Rand}; use std::rt::local::Local; -use std::rt::rtio::{RemoteCallback, PausibleIdleCallback, Callback, EventLoop}; +use std::rt::rtio::{RemoteCallback, PausableIdleCallback, Callback, EventLoop}; use std::rt::task::BlockedTask; use std::rt::task::Task; use std::sync::deque; @@ -779,6 +779,9 @@ impl Scheduler { /// randomness is a result of performing a round of work stealing (which /// may end up stealing from the current scheduler). pub fn yield_now(mut ~self, cur: ~GreenTask) { + // Async handles trigger the scheduler by calling yield_now on the local + // task, which eventually gets us to here. See comments in SchedRunner + // for more info on this. if cur.is_sched() { assert!(self.sched_task.is_none()); self.run_sched_once(cur); @@ -1345,7 +1348,7 @@ mod test { impl Drop for S { fn drop(&mut self) { - let _foo = @0; + let _foo = ~0; } } diff --git a/src/libnative/io/process.rs b/src/libnative/io/process.rs index 2277d408ee4..64ce9d7e348 100644 --- a/src/libnative/io/process.rs +++ b/src/libnative/io/process.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cast; use std::io; use std::libc::{pid_t, c_void, c_int}; use std::libc; @@ -17,6 +16,8 @@ use std::ptr; use std::rt::rtio; use p = std::io::process; +#[cfg(windows)] use std::cast; + use super::file; /** diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 60ae239ee97..e0666592651 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -15,12 +15,7 @@ //! version of I/O. #[pkgid = "native#0.9-pre"]; -#[link(name = "native", - package_id = "native", - vers = "0.9-pre", - uuid = "535344a7-890f-5a23-e1f3-e0d118805141", - url = "https://github.com/mozilla/rust/tree/master/src/native")]; - +#[crate_id = "native#0.9-pre"]; #[license = "MIT/ASL2"]; #[crate_type = "rlib"]; #[crate_type = "dylib"]; @@ -46,7 +41,7 @@ pub mod task; #[lang = "start"] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; - use std::task::try; + use std::task; do start(argc, argv) { // Instead of invoking main directly on this thread, invoke it on @@ -55,7 +50,9 @@ pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { // of the main thread's stack, so for stack overflow detection to work // we must spawn the task in a subtask which we know the stack size of. let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; - match do try { main() } { + let mut task = task::task(); + task.name("
"); + match do task.try { main() } { Ok(()) => { os::set_exit_status(0); } Err(..) => { os::set_exit_status(rt::DEFAULT_ERROR_CODE); } } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 0cf91fbba0e..214f60291fe 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -333,6 +333,10 @@ pub mod write { } unsafe fn configure_llvm(sess: Session) { + use std::unstable::mutex::{MUTEX_INIT, Mutex}; + static mut LOCK: Mutex = MUTEX_INIT; + static mut CONFIGURED: bool = false; + // Copy what clan does by turning on loop vectorization at O2 and // slp vectorization at O3 let vectorize_loop = !sess.no_vectorize_loops() && @@ -360,7 +364,13 @@ pub mod write { add(*arg); } - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); + LOCK.lock(); + if !CONFIGURED { + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); + CONFIGURED = true; + } + LOCK.unlock(); } unsafe fn populate_llvm_passes(fpm: lib::llvm::PassManagerRef, diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index ecf08df5f18..21f18eda140 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -487,8 +487,9 @@ fn lib_output_file_name(workspace: &Path, short_name: &str) -> Path { } fn output_file_name(workspace: &Path, short_name: ~str) -> Path { - target_build_dir(workspace).join(short_name.as_slice()).join(format!("{}{}", short_name, - os::EXE_SUFFIX)) + target_build_dir(workspace).join(short_name.as_slice()) + .join(format!("{}{}", short_name, + os::consts::EXE_SUFFIX)) } #[cfg(target_os = "linux")] @@ -1353,7 +1354,7 @@ fn test_import_rustpkg() { command_line_test([~"build", ~"foo"], workspace); debug!("workspace = {}", workspace.display()); assert!(target_build_dir(workspace).join("foo").join(format!("pkg{}", - os::EXE_SUFFIX)).exists()); + os::consts::EXE_SUFFIX)).exists()); } #[test] @@ -1366,7 +1367,7 @@ fn test_macro_pkg_script() { command_line_test([~"build", ~"foo"], workspace); debug!("workspace = {}", workspace.display()); assert!(target_build_dir(workspace).join("foo").join(format!("pkg{}", - os::EXE_SUFFIX)).exists()); + os::consts::EXE_SUFFIX)).exists()); } #[test] diff --git a/src/librustuv/file.rs b/src/librustuv/file.rs index 059bf072a1a..82d0fd823a3 100644 --- a/src/librustuv/file.rs +++ b/src/librustuv/file.rs @@ -18,7 +18,6 @@ use std::rt::task::BlockedTask; use std::io::{FileStat, IoError}; use std::io; use std::rt::rtio; -use std::vec; use homing::{HomingIO, HomeHandle}; use super::{Loop, UvError, uv_error_to_io_error, wait_until_woken_after, wakeup}; diff --git a/src/librustuv/idle.rs b/src/librustuv/idle.rs index 44b74d05096..80d21404e4b 100644 --- a/src/librustuv/idle.rs +++ b/src/librustuv/idle.rs @@ -100,7 +100,7 @@ mod test { use std::cast; use std::cell::RefCell; use std::rc::Rc; - use std::rt::rtio::{Callback, PausibleIdleCallback}; + use std::rt::rtio::{Callback, PausableIdleCallback}; use std::rt::task::{BlockedTask, Task}; use std::rt::local::Local; use super::IdleWatcher; diff --git a/src/librustuv/macros.rs b/src/librustuv/macros.rs index 61b4de57655..6c8c16784a1 100644 --- a/src/librustuv/macros.rs +++ b/src/librustuv/macros.rs @@ -30,14 +30,13 @@ macro_rules! uvdebug ( pub fn dumb_println(args: &fmt::Arguments) { use std::io; use std::libc; - use std::vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/librustuv/signal.rs b/src/librustuv/signal.rs index 0f81966b169..6772c6d1936 100644 --- a/src/librustuv/signal.rs +++ b/src/librustuv/signal.rs @@ -68,7 +68,7 @@ impl RtioSignal for SignalWatcher {} impl Drop for SignalWatcher { fn drop(&mut self) { let _m = self.fire_homing_missile(); - self.close_async_(); + self.close(); } } diff --git a/src/librustuv/timer.rs b/src/librustuv/timer.rs index e87090753f5..4a0ad44d311 100644 --- a/src/librustuv/timer.rs +++ b/src/librustuv/timer.rs @@ -169,7 +169,7 @@ impl Drop for TimerWatcher { let _action = { let _m = self.fire_homing_missile(); self.stop(); - self.close_async_(); + self.close(); self.action.take() }; } diff --git a/src/librustuv/uvio.rs b/src/librustuv/uvio.rs index 9e7343aa2da..dbf129d0b69 100644 --- a/src/librustuv/uvio.rs +++ b/src/librustuv/uvio.rs @@ -86,10 +86,10 @@ impl rtio::EventLoop for UvEventLoop { IdleWatcher::onetime(&mut self.uvio.loop_, f); } - fn pausible_idle_callback(&mut self, cb: ~rtio::Callback) - -> ~rtio::PausibleIdleCallback + fn pausable_idle_callback(&mut self, cb: ~rtio::Callback) + -> ~rtio::PausableIdleCallback { - IdleWatcher::new(&mut self.uvio.loop_, cb) as ~rtio::PausibleIdleCallback + IdleWatcher::new(&mut self.uvio.loop_, cb) as ~rtio::PausableIdleCallback } fn remote_callback(&mut self, f: ~rtio::Callback) -> ~rtio::RemoteCallback { diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index 59a6903adbf..01b409d4316 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -175,7 +175,8 @@ mod tests { fn connect_error() { let mut called = false; io_error::cond.trap(|e| { - assert_eq!(e.kind, OtherIoError); + assert_eq!(e.kind, + if cfg!(windows) {OtherIoError} else {FileNotFound}); called = true; }).inside(|| { let stream = UnixStream::connect(&("path/to/nowhere")); diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 5249d331f72..1e4fa7968dc 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -34,7 +34,6 @@ use libc; use option::{Option, Some, None}; use result::{Ok, Err}; use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY}; -use vec; // And so begins the tale of acquiring a uv handle to a stdio stream on all // platforms in all situations. Our story begins by splitting the world into two @@ -137,7 +136,7 @@ fn with_task_stdout(f: |&mut Writer|) { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDOUT_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index e273aedf7cc..4be11227965 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -31,6 +31,7 @@ macro_rules! iotest ( use io::net::tcp::*; use io::net::ip::*; use io::net::udp::*; + #[cfg(unix)] use io::net::unix::*; use str; use util; diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index b75f2927003..42cce272e44 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -42,7 +42,7 @@ impl Drop for Borrowed { } let val: ~T = cast::transmute(self.val); put::(val); - assert!(exists()); + rtassert!(exists()); } } } @@ -110,7 +110,7 @@ pub mod compiled { #[inline] pub unsafe fn take() -> ~T { let ptr = RT_TLS_PTR; - assert!(!ptr.is_null()); + rtassert!(!ptr.is_null()); let ptr: ~T = cast::transmute(ptr); // can't use `as`, due to type not matching with `cfg(test)` RT_TLS_PTR = cast::transmute(0); @@ -180,7 +180,7 @@ pub mod native { } pub unsafe fn cleanup() { - assert!(INITIALIZED); + rtassert!(INITIALIZED); tls::destroy(RT_TLS_KEY); LOCK.destroy(); INITIALIZED = false; diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index d0c062c1274..0dd6c883d5b 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -176,6 +176,7 @@ pub fn init(argc: int, argv: **u8) { args::init(argc, argv); env::init(); logging::init(); + local_ptr::init(); } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 97b08cc18ca..6b3d50a76ac 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -95,14 +95,16 @@ impl<'a> LocalIo<'a> { /// Returns the local I/O: either the local scheduler's I/O services or /// the native I/O services. pub fn borrow() -> Option { - // XXX: This is currently very unsafely implemented. We don't actually - // *take* the local I/O so there's a very real possibility that we - // can have two borrows at once. Currently there is not a clear way - // to actually borrow the local I/O factory safely because even if - // ownership were transferred down to the functions that the I/O - // factory implements it's just too much of a pain to know when to - // relinquish ownership back into the local task (but that would be - // the safe way of implementing this function). + // FIXME(#11053): bad + // + // This is currently very unsafely implemented. We don't actually + // *take* the local I/O so there's a very real possibility that we + // can have two borrows at once. Currently there is not a clear way + // to actually borrow the local I/O factory safely because even if + // ownership were transferred down to the functions that the I/O + // factory implements it's just too much of a pain to know when to + // relinquish ownership back into the local task (but that would be + // the safe way of implementing this function). // // In order to get around this, we just transmute a copy out of the task // in order to have what is likely a static lifetime (bad). diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 765f0b427cd..e6ab159a769 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -15,9 +15,10 @@ use any::AnyOwnExt; use borrow; +use cast; use cleanup; use io::Writer; -use libc::{c_char, size_t}; +use iter::{Iterator, Take}; use local_data; use ops::Drop; use option::{Option, Some, None}; @@ -488,7 +489,10 @@ mod test { #[test] #[should_fail] - fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) } + fn test_begin_unwind() { + use rt::unwind::begin_unwind; + begin_unwind("cause", file!(), line!()) + } // Task blocking tests diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs index 11189282f68..f4f4aaa2765 100644 --- a/src/libstd/rt/thread.rs +++ b/src/libstd/rt/thread.rs @@ -144,15 +144,11 @@ impl Drop for Thread { #[cfg(windows)] mod imp { - use super::DEFAULT_STACK_SIZE; - use cast; use libc; use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, LPVOID, DWORD, LPDWORD, HANDLE}; use ptr; - use libc; - use cast; pub type rust_thread = HANDLE; pub type rust_thread_return = DWORD; diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index 8248c6274ca..9706dbae4c6 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -10,8 +10,9 @@ // Implementation of Rust stack unwinding // -// For background on exception handling and stack unwinding please see "Exception Handling in LLVM" -// (llvm.org/docs/ExceptionHandling.html) and documents linked from it. +// For background on exception handling and stack unwinding please see +// "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and +// documents linked from it. // These are also good reads: // http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ // http://monoinfinito.wordpress.com/series/exception-handling-in-c/ @@ -20,41 +21,55 @@ // ~~~ A brief summary ~~~ // Exception handling happens in two phases: a search phase and a cleanup phase. // -// In both phases the unwinder walks stack frames from top to bottom using information from -// the stack frame unwind sections of the current process's modules ("module" here refers to -// an OS module, i.e. an executable or a dynamic library). +// In both phases the unwinder walks stack frames from top to bottom using +// information from the stack frame unwind sections of the current process's +// modules ("module" here refers to an OS module, i.e. an executable or a +// dynamic library). // -// For each stack frame, it invokes the associated "personality routine", whose address is also -// stored in the unwind info section. +// For each stack frame, it invokes the associated "personality routine", whose +// address is also stored in the unwind info section. // -// In the search phase, the job of a personality routine is to examine exception object being -// thrown, and to decide whether it should be caught at that stack frame. Once the handler frame -// has been identified, cleanup phase begins. +// In the search phase, the job of a personality routine is to examine exception +// object being thrown, and to decide whether it should be caught at that stack +// frame. Once the handler frame has been identified, cleanup phase begins. // -// In the cleanup phase, personality routines invoke cleanup code associated with their -// stack frames (i.e. destructors). Once stack has been unwound down to the handler frame level, -// unwinding stops and the last personality routine transfers control to its' catch block. +// In the cleanup phase, personality routines invoke cleanup code associated +// with their stack frames (i.e. destructors). Once stack has been unwound down +// to the handler frame level, unwinding stops and the last personality routine +// transfers control to its' catch block. // // ~~~ Frame unwind info registration ~~~ -// Each module has its' own frame unwind info section (usually ".eh_frame"), and unwinder needs -// to know about all of them in order for unwinding to be able to cross module boundaries. +// Each module has its' own frame unwind info section (usually ".eh_frame"), and +// unwinder needs to know about all of them in order for unwinding to be able to +// cross module boundaries. // -// On some platforms, like Linux, this is achieved by dynamically enumerating currently loaded -// modules via the dl_iterate_phdr() API and finding all .eh_frame sections. +// On some platforms, like Linux, this is achieved by dynamically enumerating +// currently loaded modules via the dl_iterate_phdr() API and finding all +// .eh_frame sections. // -// Others, like Windows, require modules to actively register their unwind info sections by calling -// __register_frame_info() API at startup. -// In the latter case it is essential that there is only one copy of the unwinder runtime -// in the process. This is usually achieved by linking to the dynamic version of the unwind -// runtime. +// Others, like Windows, require modules to actively register their unwind info +// sections by calling __register_frame_info() API at startup. In the latter +// case it is essential that there is only one copy of the unwinder runtime in +// the process. This is usually achieved by linking to the dynamic version of +// the unwind runtime. // // Currently Rust uses unwind runtime provided by libgcc. -use prelude::*; -use cast::transmute; -use task::TaskResult; +use any::{Any, AnyRefExt}; +use c_str::CString; +use cast; +use kinds::Send; +use libc::{c_char, size_t}; use libc::{c_void, c_int}; -use self::libunwind::*; +use option::{Some, None, Option}; +use result::{Err, Ok}; +use rt::local::Local; +use rt::task::Task; +use str::Str; +use task::TaskResult; +use unstable::intrinsics; + +use uw = self::libunwind; mod libunwind { //! Unwind library interface @@ -109,34 +124,41 @@ mod libunwind { } pub struct Unwinder { - unwinding: bool, - cause: Option<~Any> + priv unwinding: bool, + priv cause: Option<~Any> } impl Unwinder { + pub fn new() -> Unwinder { + Unwinder { + unwinding: false, + cause: None, + } + } + + pub fn unwinding(&self) -> bool { + self.unwinding + } pub fn try(&mut self, f: ||) { use unstable::raw::Closure; unsafe { - let closure: Closure = transmute(f); - let code = transmute(closure.code); - let env = transmute(closure.env); - - let ep = rust_try(try_fn, code, env); + let closure: Closure = cast::transmute(f); + let ep = rust_try(try_fn, closure.code as *c_void, + closure.env as *c_void); if !ep.is_null() { rtdebug!("Caught {}", (*ep).exception_class); - _Unwind_DeleteException(ep); + uw::_Unwind_DeleteException(ep); } } extern fn try_fn(code: *c_void, env: *c_void) { unsafe { - let closure: Closure = Closure { - code: transmute(code), - env: transmute(env), - }; - let closure: || = transmute(closure); + let closure: || = cast::transmute(Closure { + code: code as *(), + env: env as *(), + }); closure(); } } @@ -144,10 +166,11 @@ impl Unwinder { extern { // Rust's try-catch // When f(...) returns normally, the return value is null. - // When f(...) throws, the return value is a pointer to the caught exception object. + // When f(...) throws, the return value is a pointer to the caught + // exception object. fn rust_try(f: extern "C" fn(*c_void, *c_void), code: *c_void, - data: *c_void) -> *_Unwind_Exception; + data: *c_void) -> *uw::_Unwind_Exception; } } @@ -158,21 +181,21 @@ impl Unwinder { self.cause = Some(cause); unsafe { - let exception = ~_Unwind_Exception { + let exception = ~uw::_Unwind_Exception { exception_class: rust_exception_class(), exception_cleanup: exception_cleanup, private_1: 0, private_2: 0 }; - let error = _Unwind_RaiseException(transmute(exception)); + let error = uw::_Unwind_RaiseException(cast::transmute(exception)); rtabort!("Could not unwind stack, error = {}", error as int) } - extern "C" fn exception_cleanup(_unwind_code: _Unwind_Reason_Code, - exception: *_Unwind_Exception) { + extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *uw::_Unwind_Exception) { rtdebug!("exception_cleanup()"); unsafe { - let _: ~_Unwind_Exception = transmute(exception); + let _: ~uw::_Unwind_Exception = cast::transmute(exception); } } } @@ -188,68 +211,75 @@ impl Unwinder { // Rust's exception class identifier. This is used by personality routines to // determine whether the exception was thrown by their own runtime. -fn rust_exception_class() -> _Unwind_Exception_Class { - let bytes = bytes!("MOZ\0RUST"); // vendor, language - unsafe { - let ptr: *_Unwind_Exception_Class = transmute(bytes.as_ptr()); - *ptr - } +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 } - -// We could implement our personality routine in pure Rust, however exception info decoding -// is tedious. More importantly, personality routines have to handle various platform -// quirks, which are not fun to maintain. For this reason, we attempt to reuse personality -// routine of the C language: __gcc_personality_v0. +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. // -// Since C does not support exception catching, __gcc_personality_v0 simply always -// returns _URC_CONTINUE_UNWIND in search phase, and always returns _URC_INSTALL_CONTEXT -// (i.e. "invoke cleanup code") in cleanup phase. +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. // -// This is pretty close to Rust's exception handling approach, except that Rust does have -// a single "catch-all" handler at the bottom of each task's stack. +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each task's stack. // So we have two versions: -// - rust_eh_personality, used by all cleanup landing pads, which never catches, so -// the behavior of __gcc_personality_v0 is perfectly adequate there, and -// - rust_eh_personality_catch, used only by rust_try(), which always catches. This is -// achieved by overriding the return value in search phase to always say "catch!". +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// This is achieved by overriding the return value in search phase to always +// say "catch!". extern "C" { fn __gcc_personality_v0(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code; + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; } #[lang="eh_personality"] #[no_mangle] // so we can reference it by name from middle/trans/base.rs #[doc(hidden)] #[cfg(not(test))] -pub extern "C" fn rust_eh_personality(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code { +pub extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context +) -> uw::_Unwind_Reason_Code +{ unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, context) + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) } } #[no_mangle] // referenced from rust_try.ll #[doc(hidden)] #[cfg(not(test))] -pub extern "C" fn rust_eh_personality_catch(version: c_int, - actions: _Unwind_Action, - exception_class: _Unwind_Exception_Class, - ue_header: *_Unwind_Exception, - context: *_Unwind_Context) -> _Unwind_Reason_Code { - if (actions as c_int & _UA_SEARCH_PHASE as c_int) != 0 { // search phase - _URC_HANDLER_FOUND // catch! +pub extern "C" fn rust_eh_personality_catch( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *uw::_Unwind_Exception, + context: *uw::_Unwind_Context +) -> uw::_Unwind_Reason_Code +{ + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! } else { // cleanup phase unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, context) + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) } } } @@ -307,11 +337,11 @@ pub fn begin_unwind(msg: M, file: &'static str, line: uint) -> ! let n = (*task).name.as_ref() .map(|n| n.as_slice()).unwrap_or(""); - println!("task '{}' failed at '{}', {}:{}", n, msg_s, + rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s, file, line); } None => { - println!("failed at '{}', {}:{}", msg_s, file, line); + rterrln!("failed at '{}', {}:{}", msg_s, file, line); intrinsics::abort(); } } diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 69c1da39abc..730a38ce886 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -69,14 +69,13 @@ pub fn default_sched_threads() -> uint { pub fn dumb_println(args: &fmt::Arguments) { use io; use libc; - use vec; struct Stderr; impl io::Writer for Stderr { fn write(&mut self, data: &[u8]) { unsafe { libc::write(libc::STDERR_FILENO, - vec::raw::to_ptr(data) as *libc::c_void, + data.as_ptr() as *libc::c_void, data.len() as libc::size_t); } } diff --git a/src/libstd/sync/arc.rs b/src/libstd/sync/arc.rs index b405104c09a..7b94a3acc2b 100644 --- a/src/libstd/sync/arc.rs +++ b/src/libstd/sync/arc.rs @@ -32,7 +32,7 @@ use vec; /// An atomically reference counted pointer. /// /// Enforces no shared-memory safety. -//#[unsafe_no_drop_flag] FIXME: #9758 +#[unsafe_no_drop_flag] pub struct UnsafeArc { priv data: *mut ArcData, } diff --git a/src/libstd/unstable/stack.rs b/src/libstd/unstable/stack.rs index b8788b8c55c..d6cd690eaa9 100644 --- a/src/libstd/unstable/stack.rs +++ b/src/libstd/unstable/stack.rs @@ -192,6 +192,7 @@ pub unsafe fn record_sp_limit(limit: uint) { #[cfg(target_arch = "mips")] #[cfg(target_arch = "arm")] #[inline(always)] unsafe fn target_record_sp_limit(limit: uint) { + use libc::c_void; return record_sp_limit(limit as *c_void); extern { fn record_sp_limit(limit: *c_void); @@ -265,6 +266,7 @@ pub unsafe fn get_sp_limit() -> uint { #[cfg(target_arch = "mips")] #[cfg(target_arch = "arm")] #[inline(always)] unsafe fn target_get_sp_limit() -> uint { + use libc::c_void; return get_sp_limit() as uint; extern { fn get_sp_limit() -> *c_void; diff --git a/src/test/bench/rt-messaging-ping-pong.rs b/src/test/bench/rt-messaging-ping-pong.rs index 90d81aa7c3e..6eef71622c5 100644 --- a/src/test/bench/rt-messaging-ping-pong.rs +++ b/src/test/bench/rt-messaging-ping-pong.rs @@ -1,4 +1,3 @@ -// 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. // @@ -12,7 +11,6 @@ extern mod extra; use std::os; use std::uint; -use std::rt::test::spawntask_later; // This is a simple bench that creates M pairs of of tasks. These // tasks ping-pong back and forth over a pair of streams. This is a @@ -28,7 +26,7 @@ fn ping_pong_bench(n: uint, m: uint) { // Create a stream B->A let (pb,cb) = Chan::<()>::new(); - do spawntask_later() || { + do spawn() || { let chan = ca; let port = pb; n.times(|| { @@ -37,7 +35,7 @@ fn ping_pong_bench(n: uint, m: uint) { }) } - do spawntask_later() || { + do spawn() || { let chan = cb; let port = pa; n.times(|| { diff --git a/src/test/bench/rt-parfib.rs b/src/test/bench/rt-parfib.rs index ab607d9aebc..6e3c42f2a4d 100644 --- a/src/test/bench/rt-parfib.rs +++ b/src/test/bench/rt-parfib.rs @@ -12,7 +12,6 @@ extern mod extra; use std::os; use std::uint; -use std::rt::test::spawntask_later; // A simple implementation of parfib. One subtree is found in a new // task and communicated over a oneshot pipe, the other is found @@ -24,7 +23,7 @@ fn parfib(n: uint) -> uint { } let (port,chan) = Chan::new(); - do spawntask_later { + do spawn { chan.send(parfib(n-1)); }; let m2 = parfib(n-2); diff --git a/src/test/bench/shootout-spectralnorm.rs b/src/test/bench/shootout-spectralnorm.rs index 87cd01f9aad..8174347e386 100644 --- a/src/test/bench/shootout-spectralnorm.rs +++ b/src/test/bench/shootout-spectralnorm.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-test arcs no longer unwrap + extern mod extra; use std::from_str::FromStr; diff --git a/src/test/compile-fail/std-uncopyable-atomics.rs b/src/test/compile-fail/std-uncopyable-atomics.rs index a46dec7830a..57c66974fcd 100644 --- a/src/test/compile-fail/std-uncopyable-atomics.rs +++ b/src/test/compile-fail/std-uncopyable-atomics.rs @@ -12,7 +12,7 @@ #[feature(globs)]; -use std::unstable::atomics::*; +use std::sync::atomics::*; use std::ptr; fn main() { diff --git a/src/test/run-make/bootstrap-from-c-with-green/Makefile b/src/test/run-make/bootstrap-from-c-with-green/Makefile new file mode 100644 index 00000000000..7f466573da7 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +all: + $(RUSTC) lib.rs -Z gen-crate-map + ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) + $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) + $(call RUN,main) + rm $(call DYLIB,boot) + $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-green/lib.rs b/src/test/run-make/bootstrap-from-c-with-green/lib.rs new file mode 100644 index 00000000000..9a03c772f3a --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/lib.rs @@ -0,0 +1,25 @@ +// 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_id="boot#0.1"]; +#[crate_type="lib"]; +#[no_uv]; + +extern mod rustuv; +extern mod green; + +#[no_mangle] // this needs to get called from C +pub extern "C" fn foo(argc: int, argv: **u8) -> int { + do green::start(argc, argv) { + do spawn { + println!("hello"); + } + } +} diff --git a/src/test/run-make/bootstrap-from-c-with-green/main.c b/src/test/run-make/bootstrap-from-c-with-green/main.c new file mode 100644 index 00000000000..1872c1ea43b --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-green/main.c @@ -0,0 +1,16 @@ +// 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-make/bootstrap-from-c-with-native/Makefile b/src/test/run-make/bootstrap-from-c-with-native/Makefile new file mode 100644 index 00000000000..7f466573da7 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +all: + $(RUSTC) lib.rs -Z gen-crate-map + ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) + $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) + $(call RUN,main) + rm $(call DYLIB,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 new file mode 100644 index 00000000000..d0639d45fa5 --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/lib.rs @@ -0,0 +1,24 @@ +// 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_id="boot#0.1"]; +#[crate_type="lib"]; +#[no_uv]; + +extern mod native; + +#[no_mangle] // this needs to get called from C +pub extern "C" fn foo(argc: int, argv: **u8) -> int { + do native::start(argc, argv) { + do spawn { + 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 new file mode 100644 index 00000000000..1872c1ea43b --- /dev/null +++ b/src/test/run-make/bootstrap-from-c-with-native/main.c @@ -0,0 +1,16 @@ +// 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-make/bootstrap-from-c-with-uvio/Makefile b/src/test/run-make/bootstrap-from-c-with-uvio/Makefile deleted file mode 100644 index 7f466573da7..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/Makefile +++ /dev/null @@ -1,9 +0,0 @@ --include ../tools.mk - -all: - $(RUSTC) lib.rs -Z gen-crate-map - ln -nsf $(call DYLIB,boot-*) $(call DYLIB,boot) - $(CC) main.c -o $(call RUN,main) -lboot -Wl,-rpath,$(TMPDIR) - $(call RUN,main) - rm $(call DYLIB,boot) - $(call FAIL,main) diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs b/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs deleted file mode 100644 index 06a06c967f4..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/lib.rs +++ /dev/null @@ -1,25 +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_id="boot#0.1"]; -#[crate_type="lib"]; - -extern mod rustuv; // pull in uvio - -use std::rt; - -#[no_mangle] // this needs to get called from C -pub extern "C" fn foo(argc: int, argv: **u8) -> int { - do rt::start(argc, argv) { - do spawn { - println!("hello"); - } - } -} diff --git a/src/test/run-make/bootstrap-from-c-with-uvio/main.c b/src/test/run-make/bootstrap-from-c-with-uvio/main.c deleted file mode 100644 index 1872c1ea43b..00000000000 --- a/src/test/run-make/bootstrap-from-c-with-uvio/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/use.rs b/src/test/run-pass/use.rs index 56ce5397efb..013487e5803 100644 --- a/src/test/run-pass/use.rs +++ b/src/test/run-pass/use.rs @@ -28,4 +28,4 @@ mod baz { } #[start] -pub fn start(_: int, _: **u8) -> int { 3 } +pub fn start(_: int, _: **u8) -> int { 0 } -- cgit 1.4.1-3-g733a5