rt: Properly block tasks while waiting for port detach

This commit is contained in:
Brian Anderson 2012-03-04 16:52:19 -08:00
parent 4c4a2320eb
commit 1347d04bb0
5 changed files with 67 additions and 12 deletions

View File

@ -42,7 +42,9 @@ native mod rustrt {
fn new_port(unit_sz: ctypes::size_t) -> *rust_port;
fn del_port(po: *rust_port);
fn rust_port_detach(po: *rust_port);
fn rust_port_begin_detach(po: *rust_port,
yield: *ctypes::uintptr_t);
fn rust_port_end_detach(po: *rust_port);
fn get_port_id(po: *rust_port) -> port_id;
fn rust_port_size(po: *rust_port) -> ctypes::size_t;
fn port_recv(dptr: *uint, po: *rust_port,
@ -82,7 +84,17 @@ enum chan<T: send> {
resource port_ptr<T: send>(po: *rust_port) {
// Once the port is detached it's guaranteed not to receive further
// messages
rustrt::rust_port_detach(po);
let yield = 0u;
let yieldp = ptr::addr_of(yield);
rustrt::rust_port_begin_detach(po, yieldp);
if yield != 0u {
// Need to wait for the port to be detached
// FIXME: If this fails then we're going to leave our port
// in a bogus state.
task::yield();
}
rustrt::rust_port_end_detach(po);
// Drain the port so that all the still-enqueued items get dropped
while rustrt::rust_port_size(po) > 0u {
// FIXME: For some reason if we don't assign to something here

View File

@ -482,18 +482,23 @@ new_port(size_t unit_sz) {
}
extern "C" CDECL void
rust_port_detach(rust_port *port) {
rust_port_begin_detach(rust_port *port, uintptr_t *yield) {
rust_task *task = rust_task_thread::get_task();
LOG(task, comm, "rust_port_detach(0x%" PRIxPTR ")", (uintptr_t) port);
port->detach();
port->begin_detach(yield);
}
extern "C" CDECL void
rust_port_end_detach(rust_port *port) {
port->end_detach();
}
extern "C" CDECL void
del_port(rust_port *port) {
rust_task *task = rust_task_thread::get_task();
LOG(task, comm, "del_port(0x%" PRIxPTR ")", (uintptr_t) port);
A(task->thread, port->get_ref_count() == 1, "Expected port ref_count == 1");
port->deref();
A(task->thread, port->get_ref_count() == 0, "Expected port ref_count == 0");
delete port;
}
extern "C" CDECL size_t

View File

@ -20,13 +20,40 @@ rust_port::~rust_port() {
task->deref();
}
void rust_port::detach() {
void rust_port::delete_this() {
scoped_lock with(detach_lock);
if (task->blocked_on(&detach_cond)) {
// The port owner is waiting for the port to be detached
task->wakeup(&detach_cond);
}
}
void rust_port::begin_detach(uintptr_t *yield) {
*yield = false;
task->release_port(id);
// FIXME: Busy waiting until we're the only ref
deref();
scoped_lock with(detach_lock);
if (get_ref_count() != 0) {
task->block(&detach_cond, "waiting for port detach");
*yield = true;
}
}
void rust_port::end_detach() {
// FIXME: For some reason, on rare occasion we can get here without
// actually having the ref count go to 0. Possibly related to #1923
bool done = false;
while (!done) {
done = get_ref_count() == 1;
done = get_ref_count() == 0;
}
// Just take the lock to make sure that the thread that signaled
// the detach_cond isn't still holding it
scoped_lock with(detach_lock);
}
void rust_port::send(void *sptr) {

View File

@ -3,6 +3,8 @@
#include "rust_internal.h"
class port_detach_cond : public rust_cond { };
class rust_port : public kernel_owned<rust_port>, public rust_cond {
public:
RUST_ATOMIC_REFCOUNT();
@ -16,15 +18,23 @@ public:
lock_and_signal lock;
private:
// Protects blocking and signaling on detach_cond
lock_and_signal detach_lock;
port_detach_cond detach_cond;
public:
rust_port(rust_task *task, size_t unit_sz);
~rust_port();
void delete_this() { delete this; }
void delete_this();
void log_state();
void send(void *sptr);
void receive(void *dptr, uintptr_t *yield);
size_t size();
void detach();
void begin_detach(uintptr_t *yield);
void end_detach();
};
//

View File

@ -34,7 +34,8 @@ rust_str_push
rust_list_files
rust_log_console_on
rust_log_console_off
rust_port_detach
rust_port_begin_detach
rust_port_end_detach
rust_port_size
rust_process_wait
rust_ptr_eq