auto merge of #6387 : brson/rust/unstable, r=brson

r? @pcwalton

* Move `SharedMutableState`, `LittleLock`, and `Exclusive` from `core::unstable` to `core::unstable::sync`
* Modernize the `SharedMutableState` interface with methods
* Rename `SharedMutableState` to `UnsafeAtomicRcBox` to match `RcBox`.
This commit is contained in:
bors 2013-05-13 14:49:48 -07:00
commit ad5bfd600d
13 changed files with 434 additions and 424 deletions

View file

@ -31,14 +31,13 @@ use kinds::Owned;
use libc::{c_void};
use option::{Option, Some, None};
use ops::Drop;
use unstable::{Exclusive, exclusive};
use unstable::sync::{Exclusive, exclusive};
use unstable::at_exit::at_exit;
use unstable::intrinsics::atomic_cxchg;
use hashmap::HashMap;
use sys::Closure;
#[cfg(test)] use unstable::{SharedMutableState, shared_mutable_state};
#[cfg(test)] use unstable::get_shared_immutable_state;
#[cfg(test)] use unstable::sync::{UnsafeAtomicRcBox};
#[cfg(test)] use task::spawn;
#[cfg(test)] use uint;
@ -234,18 +233,16 @@ extern {
#[test]
fn test_clone_rc() {
type MyType = SharedMutableState<int>;
fn key(_v: SharedMutableState<int>) { }
fn key(_v: UnsafeAtomicRcBox<int>) { }
for uint::range(0, 100) |_| {
do spawn {
unsafe {
let val = do global_data_clone_create(key) {
~shared_mutable_state(10)
~UnsafeAtomicRcBox::new(10)
};
assert!(get_shared_immutable_state(&val) == &10);
assert!(val.get() == &10);
}
}
}
@ -253,16 +250,14 @@ fn test_clone_rc() {
#[test]
fn test_modify() {
type MyType = SharedMutableState<int>;
fn key(_v: SharedMutableState<int>) { }
fn key(_v: UnsafeAtomicRcBox<int>) { }
unsafe {
do global_data_modify(key) |v| {
match v {
None => {
unsafe {
Some(~shared_mutable_state(10))
Some(~UnsafeAtomicRcBox::new(10))
}
}
_ => fail!()
@ -272,7 +267,7 @@ fn test_modify() {
do global_data_modify(key) |v| {
match v {
Some(sms) => {
let v = get_shared_immutable_state(sms);
let v = sms.get();
assert!(*v == 10);
None
},
@ -284,7 +279,7 @@ fn test_modify() {
match v {
None => {
unsafe {
Some(~shared_mutable_state(10))
Some(~UnsafeAtomicRcBox::new(10))
}
}
_ => fail!()

View file

@ -0,0 +1,78 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[doc(hidden)];
use libc;
use comm::{GenericChan, GenericPort};
use prelude::*;
use task;
pub mod at_exit;
pub mod global;
pub mod finally;
pub mod weak_task;
pub mod exchange_alloc;
pub mod intrinsics;
pub mod simd;
pub mod extfmt;
#[cfg(not(test))]
pub mod lang;
pub mod sync;
/**
Start a new thread outside of the current runtime context and wait
for it to terminate.
The executing thread has no access to a task pointer and will be using
a normal large stack.
*/
pub fn run_in_bare_thread(f: ~fn()) {
let (port, chan) = comm::stream();
// FIXME #4525: Unfortunate that this creates an extra scheduler but it's
// necessary since rust_raw_thread_join_delete is blocking
do task::spawn_sched(task::SingleThreaded) {
unsafe {
let closure: &fn() = || {
f()
};
let thread = rust_raw_thread_start(&closure);
rust_raw_thread_join_delete(thread);
chan.send(());
}
}
port.recv();
}
#[test]
fn test_run_in_bare_thread() {
let i = 100;
do run_in_bare_thread {
assert!(i == 100);
}
}
#[test]
fn test_run_in_bare_thread_exchange() {
// Does the exchange heap work without the runtime?
let i = ~100;
do run_in_bare_thread {
assert!(i == ~100);
}
}
#[allow(non_camel_case_types)] // runtime type
pub type raw_thread = libc::c_void;
extern {
fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread;
fn rust_raw_thread_join_delete(thread: *raw_thread);
}

View file

@ -0,0 +1,286 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cast;
use libc;
use option::*;
use task;
use task::atomically;
use unstable::finally::Finally;
use unstable::intrinsics;
use ops::Drop;
use clone::Clone;
use kinds::Owned;
/// An atomically reference counted pointer.
///
/// Enforces no shared-memory safety.
pub struct UnsafeAtomicRcBox<T> {
data: *mut libc::c_void,
}
struct AtomicRcBoxData<T> {
count: int,
data: Option<T>,
}
impl<T: Owned> UnsafeAtomicRcBox<T> {
pub fn new(data: T) -> UnsafeAtomicRcBox<T> {
unsafe {
let data = ~AtomicRcBoxData { count: 1, data: Some(data) };
let ptr = cast::transmute(data);
return UnsafeAtomicRcBox { data: ptr };
}
}
#[inline(always)]
#[cfg(stage0)]
pub unsafe fn get(&self) -> *mut T
{
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count > 0);
let r: *mut T = cast::transmute(data.data.get_mut_ref());
cast::forget(data);
return r;
}
#[inline(always)]
#[cfg(not(stage0))]
pub unsafe fn get(&self) -> *mut T
{
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count > 0);
let r: *mut T = data.data.get_mut_ref();
cast::forget(data);
return r;
}
#[inline(always)]
#[cfg(stage0)]
pub unsafe fn get_immut(&self) -> *T
{
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count > 0);
let r: *T = cast::transmute(data.data.get_mut_ref());
cast::forget(data);
return r;
}
#[inline(always)]
#[cfg(not(stage0))]
pub unsafe fn get_immut(&self) -> *T
{
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count > 0);
let r: *T = cast::transmute_immut(data.data.get_mut_ref());
cast::forget(data);
return r;
}
}
impl<T: Owned> Clone for UnsafeAtomicRcBox<T> {
fn clone(&self) -> UnsafeAtomicRcBox<T> {
unsafe {
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
let new_count = intrinsics::atomic_xadd(&mut data.count, 1) + 1;
assert!(new_count >= 2);
cast::forget(data);
return UnsafeAtomicRcBox { data: self.data };
}
}
}
#[unsafe_destructor]
impl<T> Drop for UnsafeAtomicRcBox<T>{
fn finalize(&self) {
unsafe {
do task::unkillable {
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
let new_count = intrinsics::atomic_xsub(&mut data.count, 1) - 1;
assert!(new_count >= 0);
if new_count == 0 {
// drop glue takes over.
} else {
cast::forget(data);
}
}
}
}
}
/****************************************************************************/
#[allow(non_camel_case_types)] // runtime type
pub type rust_little_lock = *libc::c_void;
struct LittleLock {
l: rust_little_lock,
}
impl Drop for LittleLock {
fn finalize(&self) {
unsafe {
rust_destroy_little_lock(self.l);
}
}
}
fn LittleLock() -> LittleLock {
unsafe {
LittleLock {
l: rust_create_little_lock()
}
}
}
pub impl LittleLock {
#[inline(always)]
unsafe fn lock<T>(&self, f: &fn() -> T) -> T {
do atomically {
rust_lock_little_lock(self.l);
do (|| {
f()
}).finally {
rust_unlock_little_lock(self.l);
}
}
}
}
struct ExData<T> {
lock: LittleLock,
failed: bool,
data: T,
}
/**
* An arc over mutable data that is protected by a lock. For library use only.
*/
pub struct Exclusive<T> {
x: UnsafeAtomicRcBox<ExData<T>>
}
pub fn exclusive<T:Owned>(user_data: T) -> Exclusive<T> {
let data = ExData {
lock: LittleLock(),
failed: false,
data: user_data
};
Exclusive {
x: UnsafeAtomicRcBox::new(data)
}
}
impl<T:Owned> Clone for Exclusive<T> {
// Duplicate an exclusive ARC, as std::arc::clone.
fn clone(&self) -> Exclusive<T> {
Exclusive { x: self.x.clone() }
}
}
pub impl<T:Owned> Exclusive<T> {
// Exactly like std::arc::mutex_arc,access(), but with the little_lock
// instead of a proper mutex. Same reason for being unsafe.
//
// Currently, scheduling operations (i.e., yielding, receiving on a pipe,
// accessing the provided condition variable) are prohibited while inside
// the exclusive. Supporting that is a work in progress.
#[inline(always)]
unsafe fn with<U>(&self, f: &fn(x: &mut T) -> U) -> U {
let rec = self.x.get();
do (*rec).lock.lock {
if (*rec).failed {
fail!(
~"Poisoned exclusive - another task failed inside!");
}
(*rec).failed = true;
let result = f(&mut (*rec).data);
(*rec).failed = false;
result
}
}
#[inline(always)]
unsafe fn with_imm<U>(&self, f: &fn(x: &T) -> U) -> U {
do self.with |x| {
f(cast::transmute_immut(x))
}
}
}
fn compare_and_swap(address: &mut int, oldval: int, newval: int) -> bool {
unsafe {
let old = intrinsics::atomic_cxchg(address, oldval, newval);
old == oldval
}
}
extern {
fn rust_create_little_lock() -> rust_little_lock;
fn rust_destroy_little_lock(lock: rust_little_lock);
fn rust_lock_little_lock(lock: rust_little_lock);
fn rust_unlock_little_lock(lock: rust_little_lock);
}
#[cfg(test)]
mod tests {
use comm;
use super::exclusive;
use task;
use uint;
#[test]
fn exclusive_arc() {
let mut futures = ~[];
let num_tasks = 10;
let count = 10;
let total = exclusive(~0);
for uint::range(0, num_tasks) |_i| {
let total = total.clone();
let (port, chan) = comm::stream();
futures.push(port);
do task::spawn || {
for uint::range(0, count) |_i| {
do total.with |count| {
**count += 1;
}
}
chan.send(());
}
};
for futures.each |f| { f.recv() }
do total.with |total| {
assert!(**total == num_tasks * count)
};
}
#[test] #[should_fail] #[ignore(cfg(windows))]
fn exclusive_poison() {
// Tests that if one task fails inside of an exclusive, subsequent
// accesses will also fail.
let x = exclusive(1);
let x2 = x.clone();
do task::try || {
do x2.with |one| {
assert!(*one == 2);
}
};
do x.with |one| {
assert!(*one == 1);
}
}
}