Skip to content

Commit 73cc624

Browse files
robarnoldEric Holk
authored and
Eric Holk
committed
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.
1 parent 09921cf commit 73cc624

File tree

3 files changed

+39
-29
lines changed

3 files changed

+39
-29
lines changed

src/rt/rust_chan.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,41 @@ rust_chan *rust_chan::clone(maybe_proxy<rust_task> *target) {
116116
return new (target_task) rust_chan(target_task, port, unit_sz);
117117
}
118118

119+
/**
120+
* Cannot Yield: If the task were to unwind, the dropped ref would still
121+
* appear to be live, causing modify-after-free errors.
122+
*/
123+
void rust_chan::destroy() {
124+
A(task->sched, ref_count == 0,
125+
"Channel's ref count should be zero.");
126+
127+
if (is_associated()) {
128+
if (port->is_proxy()) {
129+
// Here is a good place to delete the port proxy we allocated
130+
// in upcall_clone_chan.
131+
rust_proxy<rust_port> *proxy = port->as_proxy();
132+
disassociate();
133+
delete proxy;
134+
} else {
135+
// We're trying to delete a channel that another task may be
136+
// reading from. We have two options:
137+
//
138+
// 1. We can flush the channel by blocking in upcall_flush_chan()
139+
// and resuming only when the channel is flushed. The problem
140+
// here is that we can get ourselves in a deadlock if the
141+
// parent task tries to join us.
142+
//
143+
// 2. We can leave the channel in a "dormnat" state by not freeing
144+
// it and letting the receiver task delete it for us instead.
145+
if (buffer.is_empty() == false) {
146+
return;
147+
}
148+
disassociate();
149+
}
150+
}
151+
delete this;
152+
}
153+
119154
//
120155
// Local Variables:
121156
// mode: C++

src/rt/rust_chan.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class rust_chan : public rc_base<rust_chan>,
2121
void send(void *sptr);
2222

2323
rust_chan *clone(maybe_proxy<rust_task> *target);
24+
25+
// Called whenever the channel's ref count drops to zero.
26+
void destroy();
2427
};
2528

2629
//

src/rt/rust_upcall.cpp

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -126,35 +126,7 @@ void upcall_del_chan(rust_task *task, rust_chan *chan) {
126126
scoped_lock with(task->kernel->scheduler_lock);
127127

128128
LOG(task, comm, "upcall del_chan(0x%" PRIxPTR ")", (uintptr_t) chan);
129-
130-
A(task->sched, chan->ref_count == 0,
131-
"Channel's ref count should be zero.");
132-
133-
if (chan->is_associated()) {
134-
if (chan->port->is_proxy()) {
135-
// Here is a good place to delete the port proxy we allocated
136-
// in upcall_clone_chan.
137-
rust_proxy<rust_port> *proxy = chan->port->as_proxy();
138-
chan->disassociate();
139-
delete proxy;
140-
} else {
141-
// We're trying to delete a channel that another task may be
142-
// reading from. We have two options:
143-
//
144-
// 1. We can flush the channel by blocking in upcall_flush_chan()
145-
// and resuming only when the channel is flushed. The problem
146-
// here is that we can get ourselves in a deadlock if the
147-
// parent task tries to join us.
148-
//
149-
// 2. We can leave the channel in a "dormant" state by not freeing
150-
// it and letting the receiver task delete it for us instead.
151-
if (chan->buffer.is_empty() == false) {
152-
return;
153-
}
154-
chan->disassociate();
155-
}
156-
}
157-
delete chan;
129+
chan->destroy();
158130
}
159131

160132
/**

0 commit comments

Comments
 (0)