about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-12-04 16:06:55 -0800
committerPatrick Walton <pcwalton@mimiga.net>2013-12-10 15:13:12 -0800
commitf08f3a757643aa16b05ec7dee73ecff216354ddf (patch)
treeaa525f71c7bace630a6fad4c7cff3b14f1b52c34 /src/libstd
parentec5603bf13ccb95c311fe5ca193a32efe07147a2 (diff)
downloadrust-f08f3a757643aa16b05ec7dee73ecff216354ddf.tar.gz
rust-f08f3a757643aa16b05ec7dee73ecff216354ddf.zip
libstd: Remove `Cell`s that were used because of `finally` by converting
their `finally` blocks to RAII.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/select.rs35
-rw-r--r--src/libstd/unstable/sync.rs69
2 files changed, 63 insertions, 41 deletions
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<T> : SelectPortInner<T> { }
 
+/// A helper type that throws away a value on a port.
+struct PortGuard<T> {
+    port: Option<comm::PortOne<T>>,
+}
+
+#[unsafe_destructor]
+impl<T:Send> Drop for PortGuard<T> {
+    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<A: Select>(ports: &mut [A]) -> uint {
@@ -56,11 +66,13 @@ pub fn select<A: 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<A: 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<T: Send>(data: T, refcount: uint) -> *mut ArcData<T> {
     cast::transmute(data)
 }
 
+/// A helper object used by `UnsafeArc::unwrap`.
+struct ChannelAndDataGuard<T> {
+    channel: Option<comm::ChanOne<bool>>,
+    data: Option<~ArcData<T>>,
+}
+
+#[unsafe_destructor]
+impl<T> Drop for ChannelAndDataGuard<T> {
+    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<T> ChannelAndDataGuard<T> {
+    fn unwrap(mut self) -> (comm::ChanOne<bool>, ~ArcData<T>) {
+        (self.channel.take_unwrap(), self.data.take_unwrap())
+    }
+}
+
 impl<T: Send> UnsafeArc<T> {
     pub fn new(data: T) -> UnsafeArc<T> {
         unsafe { UnsafeArc { data: new_inner(data, 1) } }
@@ -160,32 +188,19 @@ impl<T: Send> UnsafeArc<T> {
                     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;