libstd: Remove Cells that were used because of finally by converting

their `finally` blocks to RAII.
This commit is contained in:
Patrick Walton 2013-12-04 16:06:55 -08:00
parent ec5603bf13
commit f08f3a7576
2 changed files with 63 additions and 41 deletions

View File

@ -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.

View File

@ -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;