-#[cfg(stage1)] #[cfg(stage2)] #[cfg(stage3)]
-pub unsafe fn unwrap_shared_mutable_state<T: Send>(rc: SharedMutableState<T>)
- -> T {
- struct DeathThroes<T> {
- mut ptr: Option<~ArcData<T>>,
- mut response: Option<pipes::ChanOne<bool>>,
- drop 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() {
- pipes::send_one(move 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();
- pipes::send_one(move response, true);
- }
- }
- }
-
- do task::unkillable {
- let ptr: ~ArcData<T> = cast::reinterpret_cast(&rc.data);
- let (c1,p1) = pipes::oneshot(); // ()
- let (c2,p2) = pipes::oneshot(); // bool
- let server: UnwrapProto = ~mut Some((move c1,move p2));
- let serverp: libc::uintptr_t = cast::transmute(move server);
- // Try to put our server end in the unwrapper slot.
- rusti::atomic_cxchg(cast::reinterpret_cast(&ptr.unwrapper),
- 0, serverp as int);
- if ptr.unwrapper != 0 {
- // 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 = rusti::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(move 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(move ptr),
- response: Some(move c2) };
- let mut p1 = Some(move p1); // argh
- do task::rekillable {
- pipes::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(move ptr);
- // Also we have to free the (rejected) server endpoints.
- let _server: UnwrapProto = cast::transmute(move serverp);
- fail ~"Another task is already unwrapping this ARC!";
- }
- }
-}
-