1
Fork 0

Handle failure conditions correctly in pipes.

This commit is contained in:
Eric Holk 2012-07-10 10:58:44 -07:00
parent d07e537fc3
commit 26e6eb3d14
6 changed files with 48 additions and 19 deletions

View file

@ -47,7 +47,7 @@ extern mod rustrt {
#[rust_stack] #[rust_stack]
fn task_clear_event_reject(task: *rust_task); fn task_clear_event_reject(task: *rust_task);
fn task_wait_event(this: *rust_task) -> *libc::c_void; fn task_wait_event(this: *rust_task, killed: &mut bool) -> *libc::c_void;
fn task_signal_event(target: *rust_task, event: *libc::c_void); fn task_signal_event(target: *rust_task, event: *libc::c_void);
} }
@ -57,6 +57,16 @@ unsafe fn uniquify<T>(x: *T) -> ~T {
unsafe { unsafe::reinterpret_cast(x) } unsafe { unsafe::reinterpret_cast(x) }
} }
fn wait_event(this: *rust_task) -> *libc::c_void {
let mut killed = false;
let res = rustrt::task_wait_event(this, &mut killed);
if killed && !task::failing() {
fail "killed"
}
res
}
fn swap_state_acq(&dst: state, src: state) -> state { fn swap_state_acq(&dst: state, src: state) -> state {
unsafe { unsafe {
reinterpret_cast(rusti::atomic_xchng_acq( reinterpret_cast(rusti::atomic_xchng_acq(
@ -113,23 +123,23 @@ fn recv<T: send>(-p: recv_packet<T>) -> option<T> {
let this = rustrt::rust_get_task(); let this = rustrt::rust_get_task();
rustrt::task_clear_event_reject(this); rustrt::task_clear_event_reject(this);
p.header.blocked_task = some(this); p.header.blocked_task = some(this);
let mut first = true;
loop { loop {
rustrt::task_clear_event_reject(this);
let old_state = swap_state_acq(p.header.state, let old_state = swap_state_acq(p.header.state,
blocked); blocked);
#debug("%?", old_state); #debug("%?", old_state);
alt old_state { alt old_state {
empty { empty {
#debug("no data available on %?, going to sleep.", p_); #debug("no data available on %?, going to sleep.", p_);
rustrt::task_wait_event(this); wait_event(this);
#debug("woke up, p.state = %?", p.header.state); #debug("woke up, p.state = %?", p.header.state);
if p.header.state == full { }
let mut payload = none; blocked {
payload <-> (*p).payload; if first {
p.header.state = terminated; fail "blocking on already blocked packet"
ret some(option::unwrap(payload))
} }
} }
blocked { fail "blocking on already blocked packet" }
full { full {
let mut payload = none; let mut payload = none;
payload <-> (*p).payload; payload <-> (*p).payload;
@ -141,11 +151,12 @@ fn recv<T: send>(-p: recv_packet<T>) -> option<T> {
ret none; ret none;
} }
} }
first = false;
} }
} }
/// Returns true if messages are available. /// Returns true if messages are available.
fn peek<T: send>(p: recv_packet<T>) -> bool { pure fn peek<T: send>(p: recv_packet<T>) -> bool {
alt p.header().state { alt p.header().state {
empty { false } empty { false }
blocked { fail "peeking on blocked packet" } blocked { fail "peeking on blocked packet" }
@ -236,7 +247,7 @@ fn wait_many(pkts: ~[&a.packet_header]) -> uint {
while !data_avail { while !data_avail {
#debug("sleeping on %? packets", pkts.len()); #debug("sleeping on %? packets", pkts.len());
let event = rustrt::task_wait_event(this) as *packet_header; let event = wait_event(this) as *packet_header;
let pos = vec::position(pkts, |p| ptr::addr_of(*p) == event); let pos = vec::position(pkts, |p| ptr::addr_of(*p) == event);
alt pos { alt pos {
@ -356,7 +367,7 @@ class recv_packet<T: send> {
option::unwrap(p) option::unwrap(p)
} }
fn header() -> &self.packet_header { pure fn header() -> &self.packet_header {
alt self.p { alt self.p {
some(packet) { some(packet) {
unsafe { unsafe {

View file

@ -46,6 +46,7 @@ export future_result;
export future_task; export future_task;
export unsupervise; export unsupervise;
export run_listener; export run_listener;
export run_with;
export spawn; export spawn;
export spawn_with; export spawn_with;

View file

@ -930,11 +930,11 @@ task_clear_event_reject(rust_task *task) {
// Waits on an event, returning the pointer to the event that unblocked this // Waits on an event, returning the pointer to the event that unblocked this
// task. // task.
extern "C" void * extern "C" void *
task_wait_event(rust_task *task) { task_wait_event(rust_task *task, bool *killed) {
// TODO: we should assert that the passed in task is the currently running // TODO: we should assert that the passed in task is the currently running
// task. We wouldn't want to wait some other task. // task. We wouldn't want to wait some other task.
return task->wait_event(); return task->wait_event(killed);
} }
extern "C" void extern "C" void

View file

@ -713,16 +713,14 @@ rust_task::allow_kill() {
} }
void * void *
rust_task::wait_event() { rust_task::wait_event(bool *killed) {
scoped_lock with(state_lock); scoped_lock with(state_lock);
if(!event_reject) { if(!event_reject) {
block_locked(&event_cond, "waiting on event"); block_locked(&event_cond, "waiting on event");
bool killed = false;
state_lock.unlock(); state_lock.unlock();
yield(&killed); yield(killed);
state_lock.lock(); state_lock.lock();
// TODO: what is the right thing to do if we are killed?
} }
event_reject = false; event_reject = false;

View file

@ -316,7 +316,7 @@ public:
this->event_reject = false; this->event_reject = false;
} }
void *wait_event(); void *wait_event(bool *killed);
void signal_event(void *event); void signal_event(void *event);
void cleanup_after_turn(); void cleanup_after_turn();

View file

@ -26,5 +26,24 @@ fn main() {
} }
}); });
sleep(iotask, 1000); sleep(iotask, 100);
let b = task::builder();
task::unsupervise(b);
task::run(b, failtest);
}
// Make sure the right thing happens during failure.
fn failtest() {
let iotask = uv::global_loop::get();
let (c, p) = oneshot::init();
do task::spawn_with(c) |_c| {
fail;
}
#error("%?", recv(p));
// make sure we get killed if we missed it in the receive.
loop { task::yield() }
} }