UnsafeArc methods return unsafe pointers, so are not themselves unsafe.

This commit is contained in:
Ben Blum 2013-07-23 17:17:34 -04:00
parent fa8102ab4a
commit 6b75e92afe
3 changed files with 127 additions and 128 deletions

View file

@ -136,7 +136,7 @@ impl<T:Freeze+Send> Arc<T> {
*/
pub fn unwrap(self) -> T {
let Arc { x: x } = self;
unsafe { x.unwrap() }
x.unwrap()
}
}
@ -250,7 +250,7 @@ impl<T:Send> MutexArc<T> {
*/
pub fn unwrap(self) -> T {
let MutexArc { x: x } = self;
let inner = unsafe { x.unwrap() };
let inner = x.unwrap();
let MutexArcInner { failed: failed, data: data, _ } = inner;
if failed {
fail!(~"Can't unwrap poisoned MutexArc - another task failed inside!");
@ -469,7 +469,7 @@ impl<T:Freeze + Send> RWArc<T> {
*/
pub fn unwrap(self) -> T {
let RWArc { x: x, _ } = self;
let inner = unsafe { x.unwrap() };
let inner = x.unwrap();
let RWArcInner { failed: failed, data: data, _ } = inner;
if failed {
fail!(~"Can't unwrap poisoned RWArc - another task failed inside!")

View file

@ -328,7 +328,7 @@ impl KillHandle {
}
// Try to see if all our children are gone already.
match unsafe { self.try_unwrap() } {
match self.try_unwrap() {
// Couldn't unwrap; children still alive. Reparent entire handle as
// our own tombstone, to be unwrapped later.
Left(this) => {
@ -340,7 +340,7 @@ impl KillHandle {
// Prefer to check tombstones that were there first,
// being "more fair" at the expense of tail-recursion.
others.take().map_consume_default(true, |f| f()) && {
let mut inner = unsafe { this.take().unwrap() };
let mut inner = this.take().unwrap();
(!inner.any_child_failed) &&
inner.child_tombstones.take_map_default(true, |f| f())
}
@ -429,7 +429,7 @@ impl Death {
do self.on_exit.take_map |on_exit| {
if success {
// We succeeded, but our children might not. Need to wait for them.
let mut inner = unsafe { self.kill_handle.take_unwrap().unwrap() };
let mut inner = self.kill_handle.take_unwrap().unwrap();
if inner.any_child_failed {
success = false;
} else {
@ -555,7 +555,7 @@ mod test {
// Without another handle to child, the try unwrap should succeed.
child.reparent_children_to(&mut parent);
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
assert!(parent_inner.child_tombstones.is_none());
assert!(parent_inner.any_child_failed == false);
}
@ -570,7 +570,7 @@ mod test {
child.notify_immediate_failure();
// Without another handle to child, the try unwrap should succeed.
child.reparent_children_to(&mut parent);
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
assert!(parent_inner.child_tombstones.is_none());
// Immediate failure should have been propagated.
assert!(parent_inner.any_child_failed);
@ -592,7 +592,7 @@ mod test {
// Otherwise, due to 'link', it would try to tombstone.
child2.reparent_children_to(&mut parent);
// Should successfully unwrap even though 'link' is still alive.
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
assert!(parent_inner.child_tombstones.is_none());
// Immediate failure should have been propagated by first child.
assert!(parent_inner.any_child_failed);
@ -611,7 +611,7 @@ mod test {
// Let parent collect tombstones.
util::ignore(link);
// Must have created a tombstone
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
assert!(parent_inner.child_tombstones.take_unwrap()());
assert!(parent_inner.any_child_failed == false);
}
@ -630,7 +630,7 @@ mod test {
// Let parent collect tombstones.
util::ignore(link);
// Must have created a tombstone
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
// Failure must be seen in the tombstone.
assert!(parent_inner.child_tombstones.take_unwrap()() == false);
assert!(parent_inner.any_child_failed == false);
@ -650,7 +650,7 @@ mod test {
// Let parent collect tombstones.
util::ignore(link);
// Must have created a tombstone
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
assert!(parent_inner.child_tombstones.take_unwrap()());
assert!(parent_inner.any_child_failed == false);
}
@ -671,7 +671,7 @@ mod test {
// Let parent collect tombstones.
util::ignore(link);
// Must have created a tombstone
let mut parent_inner = unsafe { parent.unwrap() };
let mut parent_inner = parent.unwrap();
// Failure must be seen in the tombstone.
assert!(parent_inner.child_tombstones.take_unwrap()() == false);
assert!(parent_inner.any_child_failed == false);

View file

@ -93,114 +93,121 @@ impl<T: Send> UnsafeAtomicRcBox<T> {
}
#[inline]
pub unsafe fn get(&self) -> *mut T
{
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count.load(Acquire) > 0); // no barrier is really needed
let r: *mut T = data.data.get_mut_ref();
cast::forget(data);
return r;
pub fn get(&self) -> *mut T {
unsafe {
let mut data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
// FIXME(#6598) Change Acquire to Relaxed.
assert!(data.count.load(Acquire) > 0);
let r: *mut T = data.data.get_mut_ref();
cast::forget(data);
return r;
}
}
#[inline]
pub unsafe fn get_immut(&self) -> *T
{
let data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count.load(Acquire) > 0); // no barrier is really needed
let r: *T = data.data.get_ref();
cast::forget(data);
return r;
pub fn get_immut(&self) -> *T {
unsafe {
let data: ~AtomicRcBoxData<T> = cast::transmute(self.data);
assert!(data.count.load(Acquire) > 0); // no barrier is really needed
let r: *T = data.data.get_ref();
cast::forget(data);
return r;
}
}
/// Wait until all other handles are dropped, then retrieve the enclosed
/// data. See extra::arc::Arc for specific semantics documentation.
/// If called when the task is already unkillable, unwrap will unkillably
/// block; otherwise, an unwrapping task can be killed by linked failure.
pub unsafe fn unwrap(self) -> T {
pub fn unwrap(self) -> T {
let this = Cell::new(self); // argh
do task::unkillable {
let mut this = this.take();
let mut data: ~AtomicRcBoxData<T> = cast::transmute(this.data);
// Set up the unwrap protocol.
let (p1,c1) = comm::oneshot(); // ()
let (p2,c2) = comm::oneshot(); // bool
// Try to put our server end in the unwrapper slot.
// This needs no barrier -- it's protected by the release barrier on
// the xadd, and the acquire+release barrier in the destructor's xadd.
// FIXME(#6598) Change Acquire to Relaxed.
if data.unwrapper.fill(~(c1,p2), Acquire).is_none() {
// Got in. Tell this handle's destructor not to run (we are now it).
this.data = ptr::mut_null();
// Drop our own reference.
let old_count = data.count.fetch_sub(1, Release);
assert!(old_count >= 1);
if old_count == 1 {
// We were the last owner. Can unwrap immediately.
// AtomicOption's destructor will free the server endpoint.
// FIXME(#3224): it should be like this
// let ~AtomicRcBoxData { data: user_data, _ } = data;
// user_data
data.data.take_unwrap()
} else {
// The *next* person who sees the refcount hit 0 will wake us.
let p1 = Cell::new(p1); // argh
// Unlike the above one, this cell is necessary. It will get
// taken either in the do block or in the finally block.
let c2_and_data = Cell::new((c2,data));
do (|| {
do task::rekillable { p1.take().recv(); }
// Got here. Back in the 'unkillable' without getting killed.
let (c2, data) = c2_and_data.take();
c2.send(true);
unsafe {
let mut this = this.take();
let mut data: ~AtomicRcBoxData<T> = cast::transmute(this.data);
// Set up the unwrap protocol.
let (p1,c1) = comm::oneshot(); // ()
let (p2,c2) = comm::oneshot(); // bool
// Try to put our server end in the unwrapper slot.
// This needs no barrier -- it's protected by the release barrier on
// the xadd, and the acquire+release barrier in the destructor's xadd.
// FIXME(#6598) Change Acquire to Relaxed.
if data.unwrapper.fill(~(c1,p2), Acquire).is_none() {
// Got in. Tell this handle's destructor not to run (we are now it).
this.data = ptr::mut_null();
// Drop our own reference.
let old_count = data.count.fetch_sub(1, Release);
assert!(old_count >= 1);
if old_count == 1 {
// We were the last owner. Can unwrap immediately.
// AtomicOption's destructor will free the server endpoint.
// FIXME(#3224): it should be like this
// let ~AtomicRcBoxData { data: user_data, _ } = data;
// user_data
let mut data = data;
data.data.take_unwrap()
}).finally {
if task::failing() {
// Killed during wait. Because this might happen while
// someone else still holds a reference, we can't free
// the data now; the "other" last refcount will free it.
} else {
// The *next* person who sees the refcount hit 0 will wake us.
let p1 = Cell::new(p1); // argh
// Unlike the above one, this cell is necessary. It will get
// taken either in the do block or in the finally block.
let c2_and_data = Cell::new((c2,data));
do (|| {
do task::rekillable { p1.take().recv(); }
// Got here. Back in the 'unkillable' without getting killed.
let (c2, data) = c2_and_data.take();
c2.send(false);
cast::forget(data);
} else {
assert!(c2_and_data.is_empty());
c2.send(true);
// FIXME(#3224): it should be like this
// let ~AtomicRcBoxData { data: user_data, _ } = data;
// user_data
let mut data = data;
data.data.take_unwrap()
}).finally {
if task::failing() {
// Killed during wait. Because this might happen while
// someone else still holds a reference, we can't free
// the data now; the "other" last refcount will free it.
let (c2, data) = c2_and_data.take();
c2.send(false);
cast::forget(data);
} else {
assert!(c2_and_data.is_empty());
}
}
}
} else {
// If 'put' returns the server end back to us, we were rejected;
// someone else was trying to unwrap. Avoid guaranteed deadlock.
cast::forget(data);
fail!("Another task is already unwrapping this Arc!");
}
} else {
// If 'put' returns the server end back to us, we were rejected;
// someone else was trying to unwrap. Avoid guaranteed deadlock.
cast::forget(data);
fail!("Another task is already unwrapping this Arc!");
}
}
}
/// As unwrap above, but without blocking. Returns 'Left(self)' if this is
/// not the last reference; 'Right(unwrapped_data)' if so.
pub unsafe fn try_unwrap(self) -> Either<UnsafeAtomicRcBox<T>, T> {
let mut this = self; // FIXME(#4330) mutable self
let mut data: ~AtomicRcBoxData<T> = cast::transmute(this.data);
// This can of course race with anybody else who has a handle, but in
// such a case, the returned count will always be at least 2. If we
// see 1, no race was possible. All that matters is 1 or not-1.
let count = data.count.load(Acquire);
assert!(count >= 1);
// The more interesting race is one with an unwrapper. They may have
// already dropped their count -- but if so, the unwrapper pointer
// will have been set first, which the barriers ensure we will see.
// (Note: using is_empty(), not take(), to not free the unwrapper.)
if count == 1 && data.unwrapper.is_empty(Acquire) {
// Tell this handle's destructor not to run (we are now it).
this.data = ptr::mut_null();
// FIXME(#3224) as above
Right(data.data.take_unwrap())
} else {
cast::forget(data);
Left(this)
pub fn try_unwrap(self) -> Either<UnsafeAtomicRcBox<T>, T> {
unsafe {
let mut this = self; // FIXME(#4330) mutable self
let mut data: ~AtomicRcBoxData<T> = cast::transmute(this.data);
// This can of course race with anybody else who has a handle, but in
// such a case, the returned count will always be at least 2. If we
// see 1, no race was possible. All that matters is 1 or not-1.
let count = data.count.load(Acquire);
assert!(count >= 1);
// The more interesting race is one with an unwrapper. They may have
// already dropped their count -- but if so, the unwrapper pointer
// will have been set first, which the barriers ensure we will see.
// (Note: using is_empty(), not take(), to not free the unwrapper.)
if count == 1 && data.unwrapper.is_empty(Acquire) {
// Tell this handle's destructor not to run (we are now it).
this.data = ptr::mut_null();
// FIXME(#3224) as above
Right(data.data.take_unwrap())
} else {
cast::forget(data);
Left(this)
}
}
}
}
@ -370,7 +377,7 @@ impl<T:Send> Exclusive<T> {
pub fn unwrap(self) -> T {
let Exclusive { x: x } = self;
// Someday we might need to unkillably unwrap an Exclusive, but not today.
let inner = unsafe { x.unwrap() };
let inner = x.unwrap();
let ExData { data: user_data, _ } = inner; // will destroy the LittleLock
user_data
}
@ -472,51 +479,43 @@ mod tests {
#[test]
fn arclike_unwrap_basic() {
unsafe {
let x = UnsafeAtomicRcBox::new(~~"hello");
assert!(x.unwrap() == ~~"hello");
}
let x = UnsafeAtomicRcBox::new(~~"hello");
assert!(x.unwrap() == ~~"hello");
}
#[test]
fn arclike_try_unwrap() {
unsafe {
let x = UnsafeAtomicRcBox::new(~~"hello");
assert!(x.try_unwrap().expect_right("try_unwrap failed") == ~~"hello");
}
let x = UnsafeAtomicRcBox::new(~~"hello");
assert!(x.try_unwrap().expect_right("try_unwrap failed") == ~~"hello");
}
#[test]
fn arclike_try_unwrap_fail() {
unsafe {
let x = UnsafeAtomicRcBox::new(~~"hello");
let x2 = x.clone();
let left_x = x.try_unwrap();
assert!(left_x.is_left());
util::ignore(left_x);
assert!(x2.try_unwrap().expect_right("try_unwrap none") == ~~"hello");
}
let x = UnsafeAtomicRcBox::new(~~"hello");
let x2 = x.clone();
let left_x = x.try_unwrap();
assert!(left_x.is_left());
util::ignore(left_x);
assert!(x2.try_unwrap().expect_right("try_unwrap none") == ~~"hello");
}
#[test]
fn arclike_try_unwrap_unwrap_race() {
// When an unwrap and a try_unwrap race, the unwrapper should always win.
unsafe {
let x = UnsafeAtomicRcBox::new(~~"hello");
let x2 = Cell::new(x.clone());
let (p,c) = comm::stream();
do task::spawn {
c.send(());
assert!(x2.take().unwrap() == ~~"hello");
c.send(());
}
p.recv();
task::yield(); // Try to make the unwrapper get blocked first.
let left_x = x.try_unwrap();
assert!(left_x.is_left());
util::ignore(left_x);
p.recv();
let x = UnsafeAtomicRcBox::new(~~"hello");
let x2 = Cell::new(x.clone());
let (p,c) = comm::stream();
do task::spawn {
c.send(());
assert!(x2.take().unwrap() == ~~"hello");
c.send(());
}
p.recv();
task::yield(); // Try to make the unwrapper get blocked first.
let left_x = x.try_unwrap();
assert!(left_x.is_left());
util::ignore(left_x);
p.recv();
}
#[test]