2010-06-23 21:03:09 -07:00
|
|
|
#include "rust_internal.h"
|
|
|
|
#include "rust_chan.h"
|
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
/**
|
|
|
|
* Create a new rust channel and associate it with the specified port.
|
|
|
|
*/
|
2011-07-19 14:31:55 -07:00
|
|
|
rust_chan::rust_chan(rust_kernel *kernel, maybe_proxy<rust_port> *port,
|
2011-07-13 15:44:09 -07:00
|
|
|
size_t unit_sz)
|
2011-07-07 11:53:08 -07:00
|
|
|
: ref_count(1),
|
2011-07-19 14:31:55 -07:00
|
|
|
kernel(kernel),
|
2011-07-07 11:53:08 -07:00
|
|
|
port(port),
|
2011-07-18 12:02:26 -07:00
|
|
|
buffer(kernel, unit_sz) {
|
2010-07-19 14:05:18 -07:00
|
|
|
if (port) {
|
2010-07-28 16:46:13 -07:00
|
|
|
associate(port);
|
2010-07-19 14:05:18 -07:00
|
|
|
}
|
2011-07-23 19:03:02 -07:00
|
|
|
// DLOG(task->sched, comm, "new rust_chan(task=0x%" PRIxPTR
|
|
|
|
// ", port=0x%" PRIxPTR ") -> chan=0x%" PRIxPTR,
|
|
|
|
// (uintptr_t) task, (uintptr_t) port, (uintptr_t) this);
|
2010-06-23 21:03:09 -07:00
|
|
|
}
|
|
|
|
|
2010-07-19 14:05:18 -07:00
|
|
|
rust_chan::~rust_chan() {
|
2011-07-23 19:03:02 -07:00
|
|
|
// DLOG(kernel->sched, comm, "del rust_chan(task=0x%" PRIxPTR ")",
|
|
|
|
// (uintptr_t) this);
|
2010-08-09 08:15:34 -07:00
|
|
|
|
2011-07-23 19:03:02 -07:00
|
|
|
// A(kernel->sched, is_associated() == false,
|
|
|
|
// "Channel must be disassociated before being freed.");
|
2010-06-23 21:03:09 -07:00
|
|
|
}
|
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
/**
|
|
|
|
* Link this channel with the specified port.
|
|
|
|
*/
|
|
|
|
void rust_chan::associate(maybe_proxy<rust_port> *port) {
|
|
|
|
this->port = port;
|
2010-08-09 08:15:34 -07:00
|
|
|
if (port->is_proxy() == false) {
|
2011-07-23 12:21:23 -07:00
|
|
|
scoped_lock with(port->referent()->lock);
|
2011-07-23 19:03:02 -07:00
|
|
|
// DLOG(kernel->sched, task,
|
|
|
|
// "associating chan: 0x%" PRIxPTR " with port: 0x%" PRIxPTR,
|
|
|
|
// this, port);
|
2011-06-22 15:44:47 -07:00
|
|
|
++this->ref_count;
|
2011-07-18 12:02:26 -07:00
|
|
|
this->task = port->referent()->task;
|
2011-07-19 14:31:55 -07:00
|
|
|
this->task->ref();
|
2010-09-07 18:39:07 -07:00
|
|
|
this->port->referent()->chans.push(this);
|
2010-07-28 16:46:13 -07:00
|
|
|
}
|
|
|
|
}
|
2010-06-23 21:03:09 -07:00
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
bool rust_chan::is_associated() {
|
|
|
|
return port != NULL;
|
|
|
|
}
|
2010-06-23 21:03:09 -07:00
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
/**
|
|
|
|
* Unlink this channel from its associated port.
|
|
|
|
*/
|
|
|
|
void rust_chan::disassociate() {
|
2011-07-23 19:03:02 -07:00
|
|
|
// A(kernel->sched, is_associated(),
|
|
|
|
// "Channel must be associated with a port.");
|
2010-07-19 14:05:18 -07:00
|
|
|
|
2010-08-09 08:15:34 -07:00
|
|
|
if (port->is_proxy() == false) {
|
2011-07-23 12:21:23 -07:00
|
|
|
scoped_lock with(port->referent()->lock);
|
2011-07-23 19:03:02 -07:00
|
|
|
// DLOG(kernel->sched, task,
|
|
|
|
// "disassociating chan: 0x%" PRIxPTR " from port: 0x%" PRIxPTR,
|
|
|
|
// this, port->referent());
|
2011-06-22 15:44:47 -07:00
|
|
|
--this->ref_count;
|
2011-07-19 14:31:55 -07:00
|
|
|
--this->task->ref_count;
|
2011-07-18 12:02:26 -07:00
|
|
|
this->task = NULL;
|
2010-09-07 18:39:07 -07:00
|
|
|
port->referent()->chans.swap_delete(this);
|
2010-08-09 08:15:34 -07:00
|
|
|
}
|
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
// Delete reference to the port.
|
|
|
|
port = NULL;
|
2010-07-19 14:05:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-08-09 08:15:34 -07:00
|
|
|
* Attempt to send data to the associated port.
|
2010-07-19 14:05:18 -07:00
|
|
|
*/
|
2010-08-09 08:15:34 -07:00
|
|
|
void rust_chan::send(void *sptr) {
|
2011-07-23 19:03:02 -07:00
|
|
|
// rust_scheduler *sched = kernel->sched;
|
|
|
|
// I(sched, !port->is_proxy());
|
2011-07-21 12:11:05 -07:00
|
|
|
|
|
|
|
rust_port *target_port = port->referent();
|
|
|
|
// TODO: We can probably avoid this lock by using atomic operations in
|
|
|
|
// circular_buffer.
|
|
|
|
scoped_lock with(target_port->lock);
|
|
|
|
|
2010-08-09 08:15:34 -07:00
|
|
|
buffer.enqueue(sptr);
|
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
if (!is_associated()) {
|
2011-07-23 19:03:02 -07:00
|
|
|
// W(sched, is_associated(),
|
|
|
|
// "rust_chan::transmit with no associated port.");
|
2010-07-28 16:46:13 -07:00
|
|
|
return;
|
2010-07-19 14:05:18 -07:00
|
|
|
}
|
|
|
|
|
2011-07-23 19:03:02 -07:00
|
|
|
// A(sched, !buffer.is_empty(),
|
|
|
|
// "rust_chan::transmit with nothing to send.");
|
2010-07-19 14:05:18 -07:00
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
if (port->is_proxy()) {
|
2010-09-07 18:39:07 -07:00
|
|
|
data_message::send(buffer.peek(), buffer.unit_sz, "send data",
|
|
|
|
task->get_handle(), port->as_proxy()->handle());
|
2010-07-28 16:46:13 -07:00
|
|
|
buffer.dequeue(NULL);
|
|
|
|
} else {
|
|
|
|
if (target_port->task->blocked_on(target_port)) {
|
2011-07-23 19:03:02 -07:00
|
|
|
// DLOG(sched, comm, "dequeued in rendezvous_ptr");
|
2010-07-28 16:46:13 -07:00
|
|
|
buffer.dequeue(target_port->task->rendezvous_ptr);
|
|
|
|
target_port->task->rendezvous_ptr = 0;
|
|
|
|
target_port->task->wakeup(target_port);
|
|
|
|
return;
|
|
|
|
}
|
2010-07-19 14:05:18 -07:00
|
|
|
}
|
|
|
|
|
2010-07-28 16:46:13 -07:00
|
|
|
return;
|
2010-06-23 21:03:09 -07:00
|
|
|
}
|
2011-06-29 20:54:03 -07:00
|
|
|
|
|
|
|
rust_chan *rust_chan::clone(maybe_proxy<rust_task> *target) {
|
|
|
|
size_t unit_sz = buffer.unit_sz;
|
|
|
|
maybe_proxy<rust_port> *port = this->port;
|
|
|
|
rust_task *target_task = NULL;
|
|
|
|
if (target->is_proxy() == false) {
|
|
|
|
port = this->port;
|
|
|
|
target_task = target->referent();
|
|
|
|
} else {
|
|
|
|
rust_handle<rust_port> *handle =
|
|
|
|
task->sched->kernel->get_port_handle(port->as_referent());
|
|
|
|
maybe_proxy<rust_port> *proxy = new rust_proxy<rust_port> (handle);
|
2011-07-23 19:03:02 -07:00
|
|
|
DLOG(task->sched, mem, "new proxy: " PTR, proxy);
|
2011-06-29 20:54:03 -07:00
|
|
|
port = proxy;
|
|
|
|
target_task = target->as_proxy()->handle()->referent();
|
|
|
|
}
|
2011-07-18 12:02:26 -07:00
|
|
|
return new (target_task->kernel, "cloned chan")
|
2011-07-19 14:31:55 -07:00
|
|
|
rust_chan(kernel, port, unit_sz);
|
2011-06-29 20:54:03 -07:00
|
|
|
}
|
|
|
|
|
2011-06-30 08:29:35 -07:00
|
|
|
/**
|
|
|
|
* 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() {
|
2011-07-23 19:03:02 -07:00
|
|
|
// A(kernel->sched, ref_count == 0,
|
|
|
|
// "Channel's ref count should be zero.");
|
2011-06-30 08:29:35 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-07-28 00:36:35 -07:00
|
|
|
//
|
|
|
|
// Local Variables:
|
|
|
|
// mode: C++
|
|
|
|
// fill-column: 78;
|
|
|
|
// indent-tabs-mode: nil
|
|
|
|
// c-basic-offset: 4
|
|
|
|
// buffer-file-coding-system: utf-8-unix
|
2011-07-13 13:51:20 -07:00
|
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
2010-07-28 00:36:35 -07:00
|
|
|
// End:
|
|
|
|
//
|