diff options
| author | Rob Arnold <robarnold@cs.cmu.edu> | 2011-06-30 08:29:35 -0700 |
|---|---|---|
| committer | Eric Holk <eholk@mozilla.com> | 2011-07-01 16:59:10 -0700 |
| commit | 73cc624e8e326f54eb0ea8bff70388d62dccd3cb (patch) | |
| tree | d0623428177fdc2f305e749eab9b03a80459d8d9 /src/rt | |
| parent | 09921cf86f156208741243a7e0ea55b88155ec72 (diff) | |
| download | rust-73cc624e8e326f54eb0ea8bff70388d62dccd3cb.tar.gz rust-73cc624e8e326f54eb0ea8bff70388d62dccd3cb.zip | |
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.
Diffstat (limited to 'src/rt')
| -rw-r--r-- | src/rt/rust_chan.cpp | 35 | ||||
| -rw-r--r-- | src/rt/rust_chan.h | 3 | ||||
| -rw-r--r-- | 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 a1c71b049ea..3065a55e582 100644 --- a/src/rt/rust_chan.cpp +++ b/src/rt/rust_chan.cpp @@ -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++ diff --git a/src/rt/rust_chan.h b/src/rt/rust_chan.h index 603936d5b77..a327dbf0c95 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<rust_task> *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 2d3f8c7e139..05220f02606 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<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(); } /** |
