Move the channel destroy code into rust_chan.

This lets native code more easily destroy channels since directly deleting a
channel is not always the right way to destroy it.
This commit is contained in:
Rob Arnold 2011-06-30 08:29:35 -07:00 committed by Eric Holk
parent 09921cf86f
commit 73cc624e8e
3 changed files with 39 additions and 29 deletions

View File

@ -116,6 +116,41 @@ rust_chan *rust_chan::clone(maybe_proxy<rust_task> *target) {
return new (target_task) rust_chan(target_task, port, unit_sz);
}
/**
* Cannot Yield: If the task were to unwind, the dropped ref would still
* appear to be live, causing modify-after-free errors.
*/
void rust_chan::destroy() {
A(task->sched, ref_count == 0,
"Channel's ref count should be zero.");
if (is_associated()) {
if (port->is_proxy()) {
// Here is a good place to delete the port proxy we allocated
// in upcall_clone_chan.
rust_proxy<rust_port> *proxy = port->as_proxy();
disassociate();
delete proxy;
} else {
// We're trying to delete a channel that another task may be
// reading from. We have two options:
//
// 1. We can flush the channel by blocking in upcall_flush_chan()
// and resuming only when the channel is flushed. The problem
// here is that we can get ourselves in a deadlock if the
// parent task tries to join us.
//
// 2. We can leave the channel in a "dormnat" state by not freeing
// it and letting the receiver task delete it for us instead.
if (buffer.is_empty() == false) {
return;
}
disassociate();
}
}
delete this;
}
//
// Local Variables:
// mode: C++

View File

@ -21,6 +21,9 @@ public:
void send(void *sptr);
rust_chan *clone(maybe_proxy<rust_task> *target);
// Called whenever the channel's ref count drops to zero.
void destroy();
};
//

View File

@ -126,35 +126,7 @@ void upcall_del_chan(rust_task *task, rust_chan *chan) {
scoped_lock with(task->kernel->scheduler_lock);
LOG(task, comm, "upcall del_chan(0x%" PRIxPTR ")", (uintptr_t) chan);
A(task->sched, chan->ref_count == 0,
"Channel's ref count should be zero.");
if (chan->is_associated()) {
if (chan->port->is_proxy()) {
// Here is a good place to delete the port proxy we allocated
// in upcall_clone_chan.
rust_proxy<rust_port> *proxy = chan->port->as_proxy();
chan->disassociate();
delete proxy;
} else {
// We're trying to delete a channel that another task may be
// reading from. We have two options:
//
// 1. We can flush the channel by blocking in upcall_flush_chan()
// and resuming only when the channel is flushed. The problem
// here is that we can get ourselves in a deadlock if the
// parent task tries to join us.
//
// 2. We can leave the channel in a "dormant" state by not freeing
// it and letting the receiver task delete it for us instead.
if (chan->buffer.is_empty() == false) {
return;
}
chan->disassociate();
}
}
delete chan;
chan->destroy();
}
/**