core: Move locks, atomic rc to unstable::sync
This commit is contained in:
parent
4f44624415
commit
fa1d0477ed
11 changed files with 313 additions and 295 deletions
|
@ -19,8 +19,8 @@ use option::{Option, Some, None};
|
||||||
use uint;
|
use uint;
|
||||||
use unstable;
|
use unstable;
|
||||||
use vec;
|
use vec;
|
||||||
use unstable::Exclusive;
|
|
||||||
use util::replace;
|
use util::replace;
|
||||||
|
use unstable::sync::{Exclusive, exclusive};
|
||||||
|
|
||||||
use pipes::{recv, try_recv, wait_many, peek, PacketHeader};
|
use pipes::{recv, try_recv, wait_many, peek, PacketHeader};
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ pub struct SharedChan<T> {
|
||||||
impl<T: Owned> SharedChan<T> {
|
impl<T: Owned> SharedChan<T> {
|
||||||
/// Converts a `chan` into a `shared_chan`.
|
/// Converts a `chan` into a `shared_chan`.
|
||||||
pub fn new(c: Chan<T>) -> SharedChan<T> {
|
pub fn new(c: Chan<T>) -> SharedChan<T> {
|
||||||
SharedChan { ch: unstable::exclusive(c) }
|
SharedChan { ch: exclusive(c) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ FIXME #4726: It would probably be appropriate to make this a real global
|
||||||
*/
|
*/
|
||||||
fn with_env_lock<T>(f: &fn() -> T) -> T {
|
fn with_env_lock<T>(f: &fn() -> T) -> T {
|
||||||
use unstable::global::global_data_clone_create;
|
use unstable::global::global_data_clone_create;
|
||||||
use unstable::{Exclusive, exclusive};
|
use unstable::sync::{Exclusive, exclusive};
|
||||||
|
|
||||||
struct SharedValue(());
|
struct SharedValue(());
|
||||||
type ValueMutex = Exclusive<SharedValue>;
|
type ValueMutex = Exclusive<SharedValue>;
|
||||||
|
@ -860,7 +860,7 @@ pub fn change_dir(p: &Path) -> bool {
|
||||||
/// is otherwise unsuccessful.
|
/// is otherwise unsuccessful.
|
||||||
pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
|
pub fn change_dir_locked(p: &Path, action: &fn()) -> bool {
|
||||||
use unstable::global::global_data_clone_create;
|
use unstable::global::global_data_clone_create;
|
||||||
use unstable::{Exclusive, exclusive};
|
use unstable::sync::{Exclusive, exclusive};
|
||||||
|
|
||||||
fn key(_: Exclusive<()>) { }
|
fn key(_: Exclusive<()>) { }
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,7 @@ use task::{ExistingScheduler, SchedulerHandle};
|
||||||
use task::unkillable;
|
use task::unkillable;
|
||||||
use uint;
|
use uint;
|
||||||
use util;
|
use util;
|
||||||
|
use unstable::sync::{Exclusive, exclusive};
|
||||||
|
|
||||||
#[cfg(test)] use task::default_task_opts;
|
#[cfg(test)] use task::default_task_opts;
|
||||||
|
|
||||||
|
@ -128,7 +129,7 @@ struct TaskGroupData {
|
||||||
// tasks in this group.
|
// tasks in this group.
|
||||||
descendants: TaskSet,
|
descendants: TaskSet,
|
||||||
}
|
}
|
||||||
type TaskGroupArc = unstable::Exclusive<Option<TaskGroupData>>;
|
type TaskGroupArc = Exclusive<Option<TaskGroupData>>;
|
||||||
|
|
||||||
type TaskGroupInner<'self> = &'self mut Option<TaskGroupData>;
|
type TaskGroupInner<'self> = &'self mut Option<TaskGroupData>;
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ struct AncestorNode {
|
||||||
ancestors: AncestorList,
|
ancestors: AncestorList,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AncestorList(Option<unstable::Exclusive<AncestorNode>>);
|
struct AncestorList(Option<Exclusive<AncestorNode>>);
|
||||||
|
|
||||||
// Accessors for taskgroup arcs and ancestor arcs that wrap the unsafety.
|
// Accessors for taskgroup arcs and ancestor arcs that wrap the unsafety.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -167,7 +168,7 @@ fn access_group<U>(x: &TaskGroupArc, blk: &fn(TaskGroupInner) -> U) -> U {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn access_ancestors<U>(x: &unstable::Exclusive<AncestorNode>,
|
fn access_ancestors<U>(x: &Exclusive<AncestorNode>,
|
||||||
blk: &fn(x: &mut AncestorNode) -> U) -> U {
|
blk: &fn(x: &mut AncestorNode) -> U) -> U {
|
||||||
x.with(blk)
|
x.with(blk)
|
||||||
}
|
}
|
||||||
|
@ -479,7 +480,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
|
||||||
// here.
|
// here.
|
||||||
let mut members = new_taskset();
|
let mut members = new_taskset();
|
||||||
taskset_insert(&mut members, spawner);
|
taskset_insert(&mut members, spawner);
|
||||||
let tasks = unstable::exclusive(Some(TaskGroupData {
|
let tasks = exclusive(Some(TaskGroupData {
|
||||||
members: members,
|
members: members,
|
||||||
descendants: new_taskset(),
|
descendants: new_taskset(),
|
||||||
}));
|
}));
|
||||||
|
@ -508,7 +509,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
|
||||||
(g, a, spawner_group.is_main)
|
(g, a, spawner_group.is_main)
|
||||||
} else {
|
} else {
|
||||||
// Child is in a separate group from spawner.
|
// Child is in a separate group from spawner.
|
||||||
let g = unstable::exclusive(Some(TaskGroupData {
|
let g = exclusive(Some(TaskGroupData {
|
||||||
members: new_taskset(),
|
members: new_taskset(),
|
||||||
descendants: new_taskset(),
|
descendants: new_taskset(),
|
||||||
}));
|
}));
|
||||||
|
@ -528,7 +529,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
|
||||||
};
|
};
|
||||||
assert!(new_generation < uint::max_value);
|
assert!(new_generation < uint::max_value);
|
||||||
// Build a new node in the ancestor list.
|
// Build a new node in the ancestor list.
|
||||||
AncestorList(Some(unstable::exclusive(AncestorNode {
|
AncestorList(Some(exclusive(AncestorNode {
|
||||||
generation: new_generation,
|
generation: new_generation,
|
||||||
parent_group: Some(spawner_group.tasks.clone()),
|
parent_group: Some(spawner_group.tasks.clone()),
|
||||||
ancestors: old_ancestors,
|
ancestors: old_ancestors,
|
||||||
|
|
|
@ -31,14 +31,14 @@ use kinds::Owned;
|
||||||
use libc::{c_void};
|
use libc::{c_void};
|
||||||
use option::{Option, Some, None};
|
use option::{Option, Some, None};
|
||||||
use ops::Drop;
|
use ops::Drop;
|
||||||
use unstable::{Exclusive, exclusive};
|
use unstable::sync::{Exclusive, exclusive};
|
||||||
use unstable::at_exit::at_exit;
|
use unstable::at_exit::at_exit;
|
||||||
use unstable::intrinsics::atomic_cxchg;
|
use unstable::intrinsics::atomic_cxchg;
|
||||||
use hashmap::HashMap;
|
use hashmap::HashMap;
|
||||||
use sys::Closure;
|
use sys::Closure;
|
||||||
|
|
||||||
#[cfg(test)] use unstable::{SharedMutableState, shared_mutable_state};
|
#[cfg(test)] use unstable::sync::{SharedMutableState, shared_mutable_state};
|
||||||
#[cfg(test)] use unstable::get_shared_immutable_state;
|
#[cfg(test)] use unstable::sync::get_shared_immutable_state;
|
||||||
#[cfg(test)] use task::spawn;
|
#[cfg(test)] use task::spawn;
|
||||||
#[cfg(test)] use uint;
|
#[cfg(test)] use uint;
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,10 @@
|
||||||
|
|
||||||
#[doc(hidden)];
|
#[doc(hidden)];
|
||||||
|
|
||||||
use cast;
|
|
||||||
use libc;
|
use libc;
|
||||||
use comm::{GenericChan, GenericPort};
|
use comm::{GenericChan, GenericPort};
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
use task;
|
use task;
|
||||||
use task::atomically;
|
|
||||||
use self::finally::Finally;
|
|
||||||
|
|
||||||
pub mod at_exit;
|
pub mod at_exit;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
|
@ -28,23 +25,7 @@ pub mod simd;
|
||||||
pub mod extfmt;
|
pub mod extfmt;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub mod lang;
|
pub mod lang;
|
||||||
|
pub mod sync;
|
||||||
mod rustrt {
|
|
||||||
use unstable::{raw_thread, rust_little_lock};
|
|
||||||
|
|
||||||
pub extern {
|
|
||||||
pub unsafe fn rust_create_little_lock() -> rust_little_lock;
|
|
||||||
pub unsafe fn rust_destroy_little_lock(lock: rust_little_lock);
|
|
||||||
pub unsafe fn rust_lock_little_lock(lock: rust_little_lock);
|
|
||||||
pub unsafe fn rust_unlock_little_lock(lock: rust_little_lock);
|
|
||||||
|
|
||||||
pub unsafe fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread;
|
|
||||||
pub unsafe fn rust_raw_thread_join_delete(thread: *raw_thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)] // runtime type
|
|
||||||
pub type raw_thread = libc::c_void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
|
@ -63,8 +44,8 @@ pub fn run_in_bare_thread(f: ~fn()) {
|
||||||
let closure: &fn() = || {
|
let closure: &fn() = || {
|
||||||
f()
|
f()
|
||||||
};
|
};
|
||||||
let thread = rustrt::rust_raw_thread_start(&closure);
|
let thread = rust_raw_thread_start(&closure);
|
||||||
rustrt::rust_raw_thread_join_delete(thread);
|
rust_raw_thread_join_delete(thread);
|
||||||
chan.send(());
|
chan.send(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,258 +69,10 @@ fn test_run_in_bare_thread_exchange() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compare_and_swap(address: &mut int, oldval: int, newval: int) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let old = intrinsics::atomic_cxchg(address, oldval, newval);
|
|
||||||
old == oldval
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Shared state & exclusive ARC
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
struct ArcData<T> {
|
|
||||||
count: libc::intptr_t,
|
|
||||||
// FIXME(#3224) should be able to make this non-option to save memory
|
|
||||||
data: Option<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArcDestruct<T> {
|
|
||||||
data: *libc::c_void,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe_destructor]
|
|
||||||
impl<T> Drop for ArcDestruct<T>{
|
|
||||||
fn finalize(&self) {
|
|
||||||
unsafe {
|
|
||||||
do task::unkillable {
|
|
||||||
let mut data: ~ArcData<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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ArcDestruct<T>(data: *libc::c_void) -> ArcDestruct<T> {
|
|
||||||
ArcDestruct {
|
|
||||||
data: data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* COMPLETELY UNSAFE. Used as a primitive for the safe versions in std::arc.
|
|
||||||
*
|
|
||||||
* Data races between tasks can result in crashes and, with sufficient
|
|
||||||
* cleverness, arbitrary type coercion.
|
|
||||||
*/
|
|
||||||
pub type SharedMutableState<T> = ArcDestruct<T>;
|
|
||||||
|
|
||||||
pub unsafe fn shared_mutable_state<T:Owned>(data: T) ->
|
|
||||||
SharedMutableState<T> {
|
|
||||||
let data = ~ArcData { count: 1, data: Some(data) };
|
|
||||||
let ptr = cast::transmute(data);
|
|
||||||
ArcDestruct(ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn get_shared_mutable_state<T:Owned>(
|
|
||||||
rc: *SharedMutableState<T>) -> *mut T
|
|
||||||
{
|
|
||||||
let ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
|
||||||
assert!(ptr.count > 0);
|
|
||||||
let r = cast::transmute(ptr.data.get_ref());
|
|
||||||
cast::forget(ptr);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
pub unsafe fn get_shared_immutable_state<'a,T:Owned>(
|
|
||||||
rc: &'a SharedMutableState<T>) -> &'a T {
|
|
||||||
let ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
|
||||||
assert!(ptr.count > 0);
|
|
||||||
// Cast us back into the correct region
|
|
||||||
let r = cast::transmute_region(ptr.data.get_ref());
|
|
||||||
cast::forget(ptr);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn clone_shared_mutable_state<T:Owned>(rc: &SharedMutableState<T>)
|
|
||||||
-> SharedMutableState<T> {
|
|
||||||
let mut ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
|
||||||
let new_count = intrinsics::atomic_xadd(&mut ptr.count, 1) + 1;
|
|
||||||
assert!(new_count >= 2);
|
|
||||||
cast::forget(ptr);
|
|
||||||
ArcDestruct((*rc).data)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T:Owned> Clone for SharedMutableState<T> {
|
|
||||||
fn clone(&self) -> SharedMutableState<T> {
|
|
||||||
unsafe {
|
|
||||||
clone_shared_mutable_state(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************/
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)] // runtime type
|
#[allow(non_camel_case_types)] // runtime type
|
||||||
pub type rust_little_lock = *libc::c_void;
|
pub type raw_thread = libc::c_void;
|
||||||
|
|
||||||
struct LittleLock {
|
extern {
|
||||||
l: rust_little_lock,
|
fn rust_raw_thread_start(f: &(&fn())) -> *raw_thread;
|
||||||
}
|
fn rust_raw_thread_join_delete(thread: *raw_thread);
|
||||||
|
|
||||||
impl Drop for LittleLock {
|
|
||||||
fn finalize(&self) {
|
|
||||||
unsafe {
|
|
||||||
rustrt::rust_destroy_little_lock(self.l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn LittleLock() -> LittleLock {
|
|
||||||
unsafe {
|
|
||||||
LittleLock {
|
|
||||||
l: rustrt::rust_create_little_lock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub impl LittleLock {
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn lock<T>(&self, f: &fn() -> T) -> T {
|
|
||||||
do atomically {
|
|
||||||
rustrt::rust_lock_little_lock(self.l);
|
|
||||||
do (|| {
|
|
||||||
f()
|
|
||||||
}).finally {
|
|
||||||
rustrt::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: SharedMutableState<ExData<T>>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exclusive<T:Owned>(user_data: T) -> Exclusive<T> {
|
|
||||||
let data = ExData {
|
|
||||||
lock: LittleLock(),
|
|
||||||
failed: false,
|
|
||||||
data: user_data
|
|
||||||
};
|
|
||||||
Exclusive {
|
|
||||||
x: unsafe {
|
|
||||||
shared_mutable_state(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T:Owned> Clone for Exclusive<T> {
|
|
||||||
// Duplicate an exclusive ARC, as std::arc::clone.
|
|
||||||
fn clone(&self) -> Exclusive<T> {
|
|
||||||
Exclusive { x: unsafe { clone_shared_mutable_state(&self.x) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = get_shared_mutable_state(&self.x);
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
283
src/libcore/unstable/sync.rs
Normal file
283
src/libcore/unstable/sync.rs
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Shared state & exclusive ARC
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
struct ArcData<T> {
|
||||||
|
count: libc::intptr_t,
|
||||||
|
// FIXME(#3224) should be able to make this non-option to save memory
|
||||||
|
data: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ArcDestruct<T> {
|
||||||
|
data: *libc::c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe_destructor]
|
||||||
|
impl<T> Drop for ArcDestruct<T>{
|
||||||
|
fn finalize(&self) {
|
||||||
|
unsafe {
|
||||||
|
do task::unkillable {
|
||||||
|
let mut data: ~ArcData<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ArcDestruct<T>(data: *libc::c_void) -> ArcDestruct<T> {
|
||||||
|
ArcDestruct {
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COMPLETELY UNSAFE. Used as a primitive for the safe versions in std::arc.
|
||||||
|
*
|
||||||
|
* Data races between tasks can result in crashes and, with sufficient
|
||||||
|
* cleverness, arbitrary type coercion.
|
||||||
|
*/
|
||||||
|
pub type SharedMutableState<T> = ArcDestruct<T>;
|
||||||
|
|
||||||
|
pub unsafe fn shared_mutable_state<T:Owned>(data: T) ->
|
||||||
|
SharedMutableState<T> {
|
||||||
|
let data = ~ArcData { count: 1, data: Some(data) };
|
||||||
|
let ptr = cast::transmute(data);
|
||||||
|
ArcDestruct(ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn get_shared_mutable_state<T:Owned>(
|
||||||
|
rc: *SharedMutableState<T>) -> *mut T
|
||||||
|
{
|
||||||
|
let ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
||||||
|
assert!(ptr.count > 0);
|
||||||
|
let r = cast::transmute(ptr.data.get_ref());
|
||||||
|
cast::forget(ptr);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub unsafe fn get_shared_immutable_state<'a,T:Owned>(
|
||||||
|
rc: &'a SharedMutableState<T>) -> &'a T {
|
||||||
|
let ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
||||||
|
assert!(ptr.count > 0);
|
||||||
|
// Cast us back into the correct region
|
||||||
|
let r = cast::transmute_region(ptr.data.get_ref());
|
||||||
|
cast::forget(ptr);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn clone_shared_mutable_state<T:Owned>(rc: &SharedMutableState<T>)
|
||||||
|
-> SharedMutableState<T> {
|
||||||
|
let mut ptr: ~ArcData<T> = cast::transmute((*rc).data);
|
||||||
|
let new_count = intrinsics::atomic_xadd(&mut ptr.count, 1) + 1;
|
||||||
|
assert!(new_count >= 2);
|
||||||
|
cast::forget(ptr);
|
||||||
|
ArcDestruct((*rc).data)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Owned> Clone for SharedMutableState<T> {
|
||||||
|
fn clone(&self) -> SharedMutableState<T> {
|
||||||
|
unsafe {
|
||||||
|
clone_shared_mutable_state(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
#[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: SharedMutableState<ExData<T>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exclusive<T:Owned>(user_data: T) -> Exclusive<T> {
|
||||||
|
let data = ExData {
|
||||||
|
lock: LittleLock(),
|
||||||
|
failed: false,
|
||||||
|
data: user_data
|
||||||
|
};
|
||||||
|
Exclusive {
|
||||||
|
x: unsafe {
|
||||||
|
shared_mutable_state(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T:Owned> Clone for Exclusive<T> {
|
||||||
|
// Duplicate an exclusive ARC, as std::arc::clone.
|
||||||
|
fn clone(&self) -> Exclusive<T> {
|
||||||
|
Exclusive { x: unsafe { clone_shared_mutable_state(&self.x) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = get_shared_mutable_state(&self.x);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3298,8 +3298,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_swap_remove_noncopyable() {
|
fn test_swap_remove_noncopyable() {
|
||||||
// Tests that we don't accidentally run destructors twice.
|
// Tests that we don't accidentally run destructors twice.
|
||||||
let mut v = ~[::unstable::exclusive(()), ::unstable::exclusive(()),
|
let mut v = ~[::unstable::sync::exclusive(()),
|
||||||
::unstable::exclusive(())];
|
::unstable::sync::exclusive(()),
|
||||||
|
::unstable::sync::exclusive(())];
|
||||||
let mut _e = v.swap_remove(0);
|
let mut _e = v.swap_remove(0);
|
||||||
assert!(v.len() == 2);
|
assert!(v.len() == 2);
|
||||||
_e = v.swap_remove(1);
|
_e = v.swap_remove(1);
|
||||||
|
|
|
@ -17,9 +17,9 @@ use sync;
|
||||||
use sync::{Mutex, mutex_with_condvars, RWlock, rwlock_with_condvars};
|
use sync::{Mutex, mutex_with_condvars, RWlock, rwlock_with_condvars};
|
||||||
|
|
||||||
use core::cast;
|
use core::cast;
|
||||||
use core::unstable::{SharedMutableState, shared_mutable_state};
|
use core::unstable::sync::{SharedMutableState, shared_mutable_state};
|
||||||
use core::unstable::{clone_shared_mutable_state};
|
use core::unstable::sync::{clone_shared_mutable_state};
|
||||||
use core::unstable::{get_shared_mutable_state, get_shared_immutable_state};
|
use core::unstable::sync::{get_shared_mutable_state, get_shared_immutable_state};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::task;
|
use core::task;
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* in std.
|
* in std.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::unstable::{Exclusive, exclusive};
|
use core::unstable::sync::{Exclusive, exclusive};
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::task;
|
use core::task;
|
||||||
use core::util;
|
use core::util;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = Some(unstable::exclusive(false));
|
let x = Some(unstable::sync::exclusive(false));
|
||||||
match x {
|
match x {
|
||||||
Some(copy z) => { //~ ERROR copying a value of non-copyable type
|
Some(copy z) => { //~ ERROR copying a value of non-copyable type
|
||||||
do z.with |b| { assert!(!*b); }
|
do z.with |b| { assert!(!*b); }
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let x = Some(unstable::exclusive(true));
|
let x = Some(unstable::sync::exclusive(true));
|
||||||
match x {
|
match x {
|
||||||
Some(ref z) if z.with(|b| *b) => {
|
Some(ref z) if z.with(|b| *b) => {
|
||||||
do z.with |b| { assert!(*b); }
|
do z.with |b| { assert!(*b); }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue