diff options
| author | Ben Blum <bblum@andrew.cmu.edu> | 2013-07-02 13:37:19 -0400 |
|---|---|---|
| committer | Ben Blum <bblum@andrew.cmu.edu> | 2013-07-20 05:08:55 -0400 |
| commit | 10a400ffaa8b67add66a62bde0ef3c415d7aed5f (patch) | |
| tree | 10c5798e9fe2a1c62ebf0cf629f7034c992314ed /src/libextra | |
| parent | 55adc4467b4364d949774d5bfd1eba4a16c0b810 (diff) | |
| download | rust-10a400ffaa8b67add66a62bde0ef3c415d7aed5f.tar.gz rust-10a400ffaa8b67add66a62bde0ef3c415d7aed5f.zip | |
Reimplement ARC::unwrap() and friends.
Diffstat (limited to 'src/libextra')
| -rw-r--r-- | src/libextra/arc.rs | 80 |
1 files changed, 72 insertions, 8 deletions
diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs index 054b4ce5177..404d5bfde58 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -50,9 +50,9 @@ use std::borrow; /// As sync::condvar, a mechanism for unlock-and-descheduling and signaling. pub struct Condvar<'self> { - is_mutex: bool, - failed: &'self mut bool, - cond: &'self sync::Condvar<'self> + priv is_mutex: bool, + priv failed: &'self mut bool, + priv cond: &'self sync::Condvar<'self> } impl<'self> Condvar<'self> { @@ -108,7 +108,7 @@ impl<'self> Condvar<'self> { ****************************************************************************/ /// An atomically reference counted wrapper for shared immutable state. -pub struct ARC<T> { x: UnsafeAtomicRcBox<T> } +pub struct ARC<T> { priv x: UnsafeAtomicRcBox<T> } /// Create an atomically reference counted wrapper. pub fn ARC<T:Freeze + Send>(data: T) -> ARC<T> { @@ -123,6 +123,20 @@ impl<T:Freeze+Send> ARC<T> { pub fn get<'a>(&'a self) -> &'a T { unsafe { &*self.x.get_immut() } } + + /** + * Retrieve the data back out of the ARC. This function blocks until the + * reference given to it is the last existing one, and then unwrap the data + * instead of destroying it. + * + * If multiple tasks call unwrap, all but the first will fail. Do not call + * unwrap from a task that holds another reference to the same ARC; it is + * guaranteed to deadlock. + */ + pub fn unwrap(self) -> T { + let ARC { x: x } = self; + unsafe { x.unwrap() } + } } /** @@ -143,9 +157,9 @@ impl<T:Freeze + Send> Clone for ARC<T> { ****************************************************************************/ #[doc(hidden)] -struct MutexARCInner<T> { lock: Mutex, failed: bool, data: T } +struct MutexARCInner<T> { priv lock: Mutex, priv failed: bool, priv data: T } /// An ARC with mutable data protected by a blocking mutex. -struct MutexARC<T> { x: UnsafeAtomicRcBox<MutexARCInner<T>> } +struct MutexARC<T> { priv x: UnsafeAtomicRcBox<MutexARCInner<T>> } /// Create a mutex-protected ARC with the supplied data. pub fn MutexARC<T:Send>(user_data: T) -> MutexARC<T> { @@ -225,6 +239,22 @@ impl<T:Send> MutexARC<T> { cond: cond }) } } + + /** + * Retrieves the data, blocking until all other references are dropped, + * exactly as arc::unwrap. + * + * Will additionally fail if another task has failed while accessing the arc. + */ + pub fn unwrap(self) -> T { + let MutexARC { x: x } = self; + let inner = unsafe { x.unwrap() }; + let MutexARCInner { failed: failed, data: data, _ } = inner; + if failed { + fail!(~"Can't unwrap poisoned MutexARC - another task failed inside!"); + } + data + } } // Common code for {mutex.access,rwlock.write}{,_cond}. @@ -268,7 +298,7 @@ fn PoisonOnFail<'r>(failed: &'r mut bool) -> PoisonOnFail { ****************************************************************************/ #[doc(hidden)] -struct RWARCInner<T> { lock: RWlock, failed: bool, data: T } +struct RWARCInner<T> { priv lock: RWlock, priv failed: bool, priv data: T } /** * A dual-mode ARC protected by a reader-writer lock. The data can be accessed * mutably or immutably, and immutably-accessing tasks may run concurrently. @@ -278,7 +308,7 @@ struct RWARCInner<T> { lock: RWlock, failed: bool, data: T } #[mutable] // XXX remove after snap #[no_freeze] struct RWARC<T> { - x: UnsafeAtomicRcBox<RWARCInner<T>>, + priv x: UnsafeAtomicRcBox<RWARCInner<T>>, } /// Create a reader/writer ARC with the supplied data. @@ -429,6 +459,23 @@ impl<T:Freeze + Send> RWARC<T> { } } } + + /** + * Retrieves the data, blocking until all other references are dropped, + * exactly as arc::unwrap. + * + * Will additionally fail if another task has failed while accessing the arc + * in write mode. + */ + pub fn unwrap(self) -> T { + let RWARC { x: x, _ } = self; + let inner = unsafe { x.unwrap() }; + let RWARCInner { failed: failed, data: data, _ } = inner; + if failed { + fail!(~"Can't unwrap poisoned RWARC - another task failed inside!") + } + data + } } // Borrowck rightly complains about immutably aliasing the rwlock in order to @@ -611,6 +658,23 @@ mod tests { } } #[test] #[should_fail] #[ignore(cfg(windows))] + pub fn test_mutex_arc_unwrap_poison() { + let arc = MutexARC(1); + let arc2 = ~(&arc).clone(); + let (p, c) = comm::stream(); + do task::spawn { + unsafe { + do arc2.access |one| { + c.send(()); + assert!(*one == 2); + } + } + } + let _ = p.recv(); + let one = arc.unwrap(); + assert!(one == 1); + } + #[test] #[should_fail] #[ignore(cfg(windows))] fn test_rw_arc_poison_wr() { let arc = ~RWARC(1); let arc2 = (*arc).clone(); |
