diff --git a/src/rt/rust_port.cpp b/src/rt/rust_port.cpp index fa7790b6025..458283fb394 100644 --- a/src/rt/rust_port.cpp +++ b/src/rt/rust_port.cpp @@ -23,6 +23,12 @@ rust_port::~rust_port() { while (chans.is_empty() == false) { rust_chan *chan = chans.peek(); chan->disassociate(); + + if (chan->ref_count == 0) { + task->log(rust_log::COMM, + "chan: 0x%" PRIxPTR " is dormant, freeing", chan); + delete chan; + } } delete remote_channel; @@ -33,13 +39,6 @@ bool rust_port::receive(void *dptr) { rust_chan *chan = chans[i]; if (chan->buffer.is_empty() == false) { chan->buffer.dequeue(dptr); - if (chan->buffer.is_empty() && chan->task->blocked()) { - task->log(rust_log::COMM, - "chan: 0x%" PRIxPTR - " is flushing, wakeup task: 0x%" PRIxPTR, - chan, chan->task); - chan->task->wakeup(this); - } task->log(rust_log::COMM, "<=== read data ==="); return true; } diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp index 7f9f0db1c6d..fb85233c570 100644 --- a/src/rt/rust_upcall.cpp +++ b/src/rt/rust_upcall.cpp @@ -108,29 +108,7 @@ upcall_new_chan(rust_task *task, rust_port *port) { extern "C" CDECL void upcall_flush_chan(rust_task *task, rust_chan *chan) { LOG_UPCALL_ENTRY(task); - rust_dom *dom = task->dom; - task->log(rust_log::UPCALL | rust_log::COMM, - "flush chan: 0x%" PRIxPTR, chan); - - if (chan->buffer.is_empty()) { - return; - } - - // We cannot flush if the target port was dropped. - if (chan->is_associated() == false) { - return; - } - - A(dom, chan->is_associated(), - "Channel should be associated to a port."); - - A(dom, chan->port->is_proxy() == false, - "Channels to remote ports should be flushed automatically."); - - // Block on the port until this channel has been completely drained - // by the port. - task->block(chan->port); - task->yield(2); + // Nop. } /** @@ -149,8 +127,19 @@ extern "C" CDECL void upcall_del_chan(rust_task *task, rust_chan *chan) { "Channel's ref count should be zero."); if (chan->is_associated()) { - A(task->dom, chan->buffer.is_empty(), - "Channel's buffer should be empty."); + // 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 (chan->buffer.is_empty() == false) { + return; + } chan->disassociate(); } delete chan;