diff options
Diffstat (limited to 'src/libcore/private.rs')
| -rw-r--r-- | src/libcore/private.rs | 190 |
1 files changed, 4 insertions, 186 deletions
diff --git a/src/libcore/private.rs b/src/libcore/private.rs index 7968fdce46e..d19951e76db 100644 --- a/src/libcore/private.rs +++ b/src/libcore/private.rs @@ -107,20 +107,9 @@ fn compare_and_swap(address: &mut int, oldval: int, newval: int) -> bool { * Shared state & exclusive ARC ****************************************************************************/ -struct UnwrapProtoInner { - contents: Option<(comm::ChanOne<()>, comm::PortOne<bool>)>, -} - -// An unwrapper uses this protocol to communicate with the "other" task that -// drops the last refcount on an arc. Unfortunately this can't be a proper -// pipe protocol because the unwrapper has to access both stages at once. -type UnwrapProto = ~UnwrapProtoInner; - struct ArcData<T> { mut count: libc::intptr_t, - mut unwrapper: int, // either a UnwrapProto or 0 - // FIXME(#3224) should be able to make this non-option to save memory, and - // in unwrap() use "let ~ArcData { data: result, _ } = thing" to unwrap it + // FIXME(#3224) should be able to make this non-option to save memory mut data: Option<T>, } @@ -131,37 +120,13 @@ struct ArcDestruct<T> { impl<T> Drop for ArcDestruct<T>{ fn finalize(&self) { unsafe { - if self.data.is_null() { - return; // Happens when destructing an unwrapper's handle. - } do task::unkillable { let data: ~ArcData<T> = cast::reinterpret_cast(&self.data); let new_count = intrinsics::atomic_xsub(&mut data.count, 1) - 1; assert new_count >= 0; if new_count == 0 { - // Were we really last, or should we hand off to an - // unwrapper? It's safe to not xchg because the unwrapper - // will set the unwrap lock *before* dropping his/her - // reference. In effect, being here means we're the only - // *awake* task with the data. - if data.unwrapper != 0 { - let mut p: UnwrapProto = - cast::reinterpret_cast(&data.unwrapper); - let (message, response) = - option::swap_unwrap(&mut p.contents); - // Send 'ready' and wait for a response. - comm::send_one(message, ()); - // Unkillable wait. Message guaranteed to come. - if comm::recv_one(response) { - // Other task got the data. - cast::forget(data); - } else { - // Other task was killed. drop glue takes over. - } - } else { - // drop glue takes over. - } + // drop glue takes over. } else { cast::forget(data); } @@ -176,79 +141,6 @@ fn ArcDestruct<T>(data: *libc::c_void) -> ArcDestruct<T> { } } -pub unsafe fn unwrap_shared_mutable_state<T:Owned>(rc: SharedMutableState<T>) - -> T { - struct DeathThroes<T> { - mut ptr: Option<~ArcData<T>>, - mut response: Option<comm::ChanOne<bool>>, - } - - impl<T> Drop for DeathThroes<T>{ - fn finalize(&self) { - unsafe { - let response = option::swap_unwrap(&mut self.response); - // In case we get killed early, we need to tell the person who - // tried to wake us whether they should hand-off the data to - // us. - if task::failing() { - comm::send_one(response, false); - // Either this swap_unwrap or the one below (at "Got - // here") ought to run. - cast::forget(option::swap_unwrap(&mut self.ptr)); - } else { - assert self.ptr.is_none(); - comm::send_one(response, true); - } - } - } - } - - do task::unkillable { - let ptr: ~ArcData<T> = cast::reinterpret_cast(&rc.data); - let (p1,c1) = comm::oneshot(); // () - let (p2,c2) = comm::oneshot(); // bool - let mut server: UnwrapProto = ~UnwrapProtoInner { - contents: Some((c1,p2)) - }; - let serverp: int = cast::transmute(server); - // Try to put our server end in the unwrapper slot. - if compare_and_swap(&mut ptr.unwrapper, 0, serverp) { - // Got in. Step 0: Tell destructor not to run. We are now it. - rc.data = ptr::null(); - // Step 1 - drop our own reference. - let new_count = intrinsics::atomic_xsub(&mut ptr.count, 1) - 1; - //assert new_count >= 0; - if new_count == 0 { - // We were the last owner. Can unwrap immediately. - // Also we have to free the server endpoints. - let _server: UnwrapProto = cast::transmute(serverp); - option::swap_unwrap(&mut ptr.data) - // drop glue takes over. - } else { - // The *next* person who sees the refcount hit 0 will wake us. - let end_result = - DeathThroes { ptr: Some(ptr), - response: Some(c2) }; - let mut p1 = Some(p1); // argh - do task::rekillable { - comm::recv_one(option::swap_unwrap(&mut p1)); - } - // Got here. Back in the 'unkillable' without getting killed. - // Recover ownership of ptr, then take the data out. - let ptr = option::swap_unwrap(&mut end_result.ptr); - option::swap_unwrap(&mut ptr.data) - // drop glue takes over. - } - } else { - // Somebody else was trying to unwrap. Avoid guaranteed deadlock. - cast::forget(ptr); - // Also we have to free the (rejected) server endpoints. - let _server: UnwrapProto = cast::transmute(serverp); - fail!(~"Another task is already unwrapping this ARC!"); - } - } -} - /** * COMPLETELY UNSAFE. Used as a primitive for the safe versions in std::arc. * @@ -259,7 +151,7 @@ pub type SharedMutableState<T> = ArcDestruct<T>; pub unsafe fn shared_mutable_state<T:Owned>(data: T) -> SharedMutableState<T> { - let data = ~ArcData { count: 1, unwrapper: 0, data: Some(data) }; + let data = ~ArcData { count: 1, data: Some(data) }; unsafe { let ptr = cast::transmute(data); ArcDestruct(ptr) @@ -413,14 +305,6 @@ pub impl<T:Owned> Exclusive<T> { } } -// FIXME(#3724) make this a by-move method on the exclusive -pub fn unwrap_exclusive<T:Owned>(arc: Exclusive<T>) -> T { - let Exclusive { x: x } = arc; - let inner = unsafe { unwrap_shared_mutable_state(x) }; - let ExData { data: data, _ } = inner; - data -} - #[cfg(test)] pub mod tests { use core::option::{None, Some}; @@ -428,7 +312,7 @@ pub mod tests { use cell::Cell; use comm; use option; - use private::{exclusive, unwrap_exclusive}; + use private::exclusive; use result; use task; use uint; @@ -479,70 +363,4 @@ pub mod tests { assert *one == 1; } } - - #[test] - pub fn exclusive_unwrap_basic() { - let x = exclusive(~~"hello"); - assert unwrap_exclusive(x) == ~~"hello"; - } - - #[test] - pub fn exclusive_unwrap_contended() { - let x = exclusive(~~"hello"); - let x2 = Cell(x.clone()); - do task::spawn { - let x2 = x2.take(); - do x2.with |_hello| { } - task::yield(); - } - assert unwrap_exclusive(x) == ~~"hello"; - - // Now try the same thing, but with the child task blocking. - let x = exclusive(~~"hello"); - let x2 = Cell(x.clone()); - let mut res = None; - do task::task().future_result(|+r| res = Some(r)).spawn { - let x2 = x2.take(); - assert unwrap_exclusive(x2) == ~~"hello"; - } - // Have to get rid of our reference before blocking. - { let _x = x; } // FIXME(#3161) util::ignore doesn't work here - let res = option::swap_unwrap(&mut res); - res.recv(); - } - - #[test] #[should_fail] #[ignore(cfg(windows))] - pub fn exclusive_unwrap_conflict() { - let x = exclusive(~~"hello"); - let x2 = Cell(x.clone()); - let mut res = None; - do task::task().future_result(|+r| res = Some(r)).spawn { - let x2 = x2.take(); - assert unwrap_exclusive(x2) == ~~"hello"; - } - assert unwrap_exclusive(x) == ~~"hello"; - let res = option::swap_unwrap(&mut res); - // See #4689 for why this can't be just "res.recv()". - assert res.recv() == task::Success; - } - - #[test] #[ignore(cfg(windows))] - pub fn exclusive_unwrap_deadlock() { - // This is not guaranteed to get to the deadlock before being killed, - // but it will show up sometimes, and if the deadlock were not there, - // the test would nondeterministically fail. - let result = do task::try { - // a task that has two references to the same exclusive will - // deadlock when it unwraps. nothing to be done about that. - let x = exclusive(~~"hello"); - let x2 = x.clone(); - do task::spawn { - for 10.times { task::yield(); } // try to let the unwrapper go - fail!(); // punt it awake from its deadlock - } - let _z = unwrap_exclusive(x); - do x2.with |_hello| { } - }; - assert result.is_err(); - } } |
