about summary refs log tree commit diff
path: root/src/rt
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
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')
-rw-r--r--src/rt/rust_chan.cpp35
-rw-r--r--src/rt/rust_chan.h3
-rw-r--r--src/rt/rust_upcall.cpp30
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();
 }
 
 /**