about summary refs log tree commit diff
path: root/src/rt/rust_chan.cpp
diff options
context:
space:
mode:
authorRob Arnold <robarnold@cs.cmu.edu>2011-06-30 08:29:35 -0700
committerEric Holk <eholk@mozilla.com>2011-07-01 16:59:10 -0700
commit73cc624e8e326f54eb0ea8bff70388d62dccd3cb (patch)
treed0623428177fdc2f305e749eab9b03a80459d8d9 /src/rt/rust_chan.cpp
parent09921cf86f156208741243a7e0ea55b88155ec72 (diff)
downloadrust-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/rust_chan.cpp')
-rw-r--r--src/rt/rust_chan.cpp35
1 files changed, 35 insertions, 0 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++