From f08f3a757643aa16b05ec7dee73ecff216354ddf Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 4 Dec 2013 16:06:55 -0800 Subject: libstd: Remove `Cell`s that were used because of `finally` by converting their `finally` blocks to RAII. --- src/libstd/select.rs | 35 ++++++++++++++--------- src/libstd/unstable/sync.rs | 69 +++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 41 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/select.rs b/src/libstd/select.rs index 4f78ade3f08..01b953051db 100644 --- a/src/libstd/select.rs +++ b/src/libstd/select.rs @@ -10,18 +10,16 @@ #[allow(missing_doc)]; -use cell::Cell; use comm; use container::Container; use iter::{Iterator, DoubleEndedIterator}; +use kinds::Send; +use ops::Drop; use option::*; -// use either::{Either, Left, Right}; -// use rt::kill::BlockedTask; use rt::local::Local; use rt::rtio::EventLoop; use rt::sched::Scheduler; use rt::shouldnt_be_public::{SelectInner, SelectPortInner}; -use unstable::finally::Finally; use vec::{OwnedVector, MutableVector}; /// Trait for message-passing primitives that can be select()ed on. @@ -32,6 +30,18 @@ pub trait Select : SelectInner { } // that implement Select on different types to use select().) pub trait SelectPort : SelectPortInner { } +/// A helper type that throws away a value on a port. +struct PortGuard { + port: Option>, +} + +#[unsafe_destructor] +impl Drop for PortGuard { + fn drop(&mut self) { + let _ = self.port.take_unwrap().recv(); + } +} + /// Receive a message from any one of many ports at once. Returns the index of the /// port whose data is ready. (If multiple are ready, returns the lowest index.) pub fn select(ports: &mut [A]) -> uint { @@ -56,11 +66,13 @@ pub fn select(ports: &mut [A]) -> uint { // after letting the task get woken up. The and_then closure needs to delay // the task from resuming until all ports have become blocked_on. let (p,c) = comm::oneshot(); - let p = Cell::new(p); - let c = Cell::new(c); - (|| { - let mut c = Some(c.take()); + { + let _guard = PortGuard { + port: Some(p), + }; + + let mut c = Some(c); let sched: ~Scheduler = Local::take(); sched.deschedule_running_task_and_then(|sched, task| { let task_handles = task.make_selectable(ports.len()); @@ -79,12 +91,7 @@ pub fn select(ports: &mut [A]) -> uint { c.send_deferred(()) } }) - }).finally(|| { - // Unkillable is necessary not because getting killed is dangerous here, - // but to force the recv not to use the same kill-flag that we used for - // selecting. Otherwise a user-sender could spuriously wakeup us here. - p.take().recv(); - }); + } // Task resumes. Now unblock ourselves from all the ports we blocked on. // If the success index wasn't reset, 'take' will just take all of them. diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index d8e437fda81..b66e551c193 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -9,7 +9,6 @@ // except according to those terms. use cast; -use cell::Cell; use comm; use ptr; use option::{Option,Some,None}; @@ -70,6 +69,35 @@ unsafe fn new_inner(data: T, refcount: uint) -> *mut ArcData { cast::transmute(data) } +/// A helper object used by `UnsafeArc::unwrap`. +struct ChannelAndDataGuard { + channel: Option>, + data: Option<~ArcData>, +} + +#[unsafe_destructor] +impl Drop for ChannelAndDataGuard { + fn drop(&mut self) { + 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. + unsafe { + let channel = self.channel.take_unwrap(); + let data = self.data.take_unwrap(); + channel.send(false); + cast::forget(data); + } + } + } +} + +impl ChannelAndDataGuard { + fn unwrap(mut self) -> (comm::ChanOne, ~ArcData) { + (self.channel.take_unwrap(), self.data.take_unwrap()) + } +} + impl UnsafeArc { pub fn new(data: T) -> UnsafeArc { unsafe { UnsafeArc { data: new_inner(data, 1) } } @@ -160,32 +188,19 @@ impl UnsafeArc { 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)); - (|| { - p1.take().recv(); - // Got here. Back in the 'unkillable' without getting killed. - let (c2, data) = c2_and_data.take(); - c2.send(true); - // FIXME(#3224): it should be like this - // let ~ArcData { 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()); - } - }) + let c2_and_data = ChannelAndDataGuard { + channel: Some(c2), + data: Some(data), + }; + p1.recv(); + // Got here. Back in the 'unkillable' without getting killed. + let (c2, data) = c2_and_data.unwrap(); + c2.send(true); + // FIXME(#3224): it should be like this + // let ~ArcData { data: user_data, _ } = data; + // user_data + let mut data = data; + data.data.take_unwrap() } } else { // If 'put' returns the server end back to us, we were rejected; -- cgit 1.4.1-3-g733a5