Skip to content

Commit 2a99163

Browse files
committed
Add UnsafeAtomicRcBox::try_unwrap()
1 parent 10a400f commit 2a99163

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

src/libstd/unstable/sync.rs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use comm;
1414
use libc;
1515
use ptr;
1616
use option::*;
17+
use either::{Either, Left, Right};
1718
use task;
1819
use task::atomically;
1920
use unstable::atomics::{AtomicOption,AtomicUint,Acquire,Release,SeqCst};
@@ -137,6 +138,31 @@ impl<T: Send> UnsafeAtomicRcBox<T> {
137138
}
138139
}
139140
}
141+
142+
/// As unwrap above, but without blocking. Returns 'Left(self)' if this is
143+
/// not the last reference; 'Right(unwrapped_data)' if so.
144+
pub unsafe fn try_unwrap(self) -> Either<UnsafeAtomicRcBox<T>, T> {
145+
let mut this = self; // FIXME(#4330) mutable self
146+
let mut data: ~AtomicRcBoxData<T> = cast::transmute(this.data);
147+
// This can of course race with anybody else who has a handle, but in
148+
// such a case, the returned count will always be at least 2. If we
149+
// see 1, no race was possible. All that matters is 1 or not-1.
150+
let count = data.count.load(Acquire);
151+
assert!(count >= 1);
152+
// The more interesting race is one with an unwrapper. They may have
153+
// already dropped their count -- but if so, the unwrapper pointer
154+
// will have been set first, which the barriers ensure we will see.
155+
// (Note: using is_empty(), not take(), to not free the unwrapper.)
156+
if count == 1 && data.unwrapper.is_empty(Acquire) {
157+
// Tell this handle's destructor not to run (we are now it).
158+
this.data = ptr::mut_null();
159+
// FIXME(#3224) as above
160+
Right(data.data.take_unwrap())
161+
} else {
162+
cast::forget(data);
163+
Left(this)
164+
}
165+
}
140166
}
141167

142168
impl<T: Send> Clone for UnsafeAtomicRcBox<T> {
@@ -380,13 +406,54 @@ mod tests {
380406
}
381407

382408
#[test]
383-
fn unsafe_unwrap_basic() {
409+
fn arclike_unwrap_basic() {
384410
unsafe {
385411
let x = UnsafeAtomicRcBox::new(~~"hello");
386412
assert!(x.unwrap() == ~~"hello");
387413
}
388414
}
389415
416+
#[test]
417+
fn arclike_try_unwrap() {
418+
unsafe {
419+
let x = UnsafeAtomicRcBox::new(~~"hello");
420+
assert!(x.try_unwrap().expect_right("try_unwrap failed") == ~~"hello");
421+
}
422+
}
423+
424+
#[test]
425+
fn arclike_try_unwrap_fail() {
426+
unsafe {
427+
let x = UnsafeAtomicRcBox::new(~~"hello");
428+
let x2 = x.clone();
429+
let left_x = x.try_unwrap();
430+
assert!(left_x.is_left());
431+
util::ignore(left_x);
432+
assert!(x2.try_unwrap().expect_right("try_unwrap none") == ~~"hello");
433+
}
434+
}
435+
436+
#[test]
437+
fn arclike_try_unwrap_unwrap_race() {
438+
// When an unwrap and a try_unwrap race, the unwrapper should always win.
439+
unsafe {
440+
let x = UnsafeAtomicRcBox::new(~~"hello");
441+
let x2 = Cell::new(x.clone());
442+
let (p,c) = comm::stream();
443+
do task::spawn {
444+
c.send(());
445+
assert!(x2.take().unwrap() == ~~"hello");
446+
c.send(());
447+
}
448+
p.recv();
449+
task::yield(); // Try to make the unwrapper get blocked first.
450+
let left_x = x.try_unwrap();
451+
assert!(left_x.is_left());
452+
util::ignore(left_x);
453+
p.recv();
454+
}
455+
}
456+
390457
#[test]
391458
fn exclusive_unwrap_basic() {
392459
// Unlike the above, also tests no double-freeing of the LittleLock.

0 commit comments

Comments
 (0)