From 73cc624e8e326f54eb0ea8bff70388d62dccd3cb Mon Sep 17 00:00:00 2001 From: Rob Arnold Date: Thu, 30 Jun 2011 08:29:35 -0700 Subject: [PATCH] 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. --- src/rt/rust_chan.cpp | 35 +++++++++++++++++++++++++++++++++++ src/rt/rust_chan.h | 3 +++ src/rt/rust_upcall.cpp | 30 +----------------------------- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/rt/rust_chan.cpp b/src/rt/rust_chan.cpp index a1c71b049ea2..3065a55e5826 100644 --- a/src/rt/rust_chan.cpp +++ b/src/rt/rust_chan.cpp @@ -116,6 +116,41 @@ rust_chan *rust_chan::clone(maybe_proxy *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 *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++ diff --git a/src/rt/rust_chan.h b/src/rt/rust_chan.h index 603936d5b771..a327dbf0c95d 100644 --- a/src/rt/rust_chan.h +++ b/src/rt/rust_chan.h @@ -21,6 +21,9 @@ public: void send(void *sptr); rust_chan *clone(maybe_proxy *target); + + // Called whenever the channel's ref count drops to zero. + void destroy(); }; // diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 2d3f8c7e1391..05220f026065 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -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 *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(); } /**