/src/rt/rust_port.cpp
C++ | 143 lines | 90 code | 29 blank | 24 comment | 9 complexity | 80f45bfb8ca05cbcd228fc4aed87c9dd MD5 | raw file
1 2#include "rust_port.h" 3#include "rust_task.h" 4 5rust_port::rust_port(rust_task *task, size_t unit_sz) 6 : ref_count(1), kernel(task->kernel), task(task), 7 unit_sz(unit_sz), buffer(kernel, unit_sz) { 8 9 LOG(task, comm, 10 "new rust_port(task=0x%" PRIxPTR ", unit_sz=%d) -> port=0x%" 11 PRIxPTR, (uintptr_t)task, unit_sz, (uintptr_t)this); 12 13 id = kernel->register_port(this); 14} 15 16rust_port::~rust_port() { 17 LOG(task, comm, "~rust_port 0x%" PRIxPTR, (uintptr_t) this); 18} 19 20void rust_port::ref() { 21 scoped_lock with(ref_lock); 22 ref_count++; 23} 24 25void rust_port::deref() { 26 scoped_lock with(ref_lock); 27 ref_count--; 28 if (!ref_count) { 29 // The port owner is waiting for the port to be detached (if it 30 // hasn't already been killed) 31 scoped_lock with(task->lifecycle_lock); 32 if (task->blocked_on(&detach_cond)) { 33 task->wakeup_inner(&detach_cond); 34 } 35 } 36} 37 38void rust_port::begin_detach(uintptr_t *yield) { 39 *yield = false; 40 41 kernel->release_port_id(id); 42 43 scoped_lock with(ref_lock); 44 ref_count--; 45 46 if (ref_count != 0) { 47 task->block(&detach_cond, "waiting for port detach"); 48 *yield = true; 49 } 50} 51 52void rust_port::end_detach() { 53 // Just take the lock to make sure that the thread that signaled 54 // the detach_cond isn't still holding it 55 scoped_lock with(ref_lock); 56 assert(ref_count == 0); 57} 58 59void rust_port::send(void *sptr) { 60 bool did_rendezvous = false; 61 { 62 scoped_lock with(lock); 63 64 buffer.enqueue(sptr); 65 66 assert(!buffer.is_empty() && 67 "rust_chan::transmit with nothing to send."); 68 69 { 70 scoped_lock with(task->lifecycle_lock); 71 if (task->blocked_on(this)) { 72 KLOG(kernel, comm, "dequeued in rendezvous_ptr"); 73 buffer.dequeue(task->rendezvous_ptr); 74 task->rendezvous_ptr = 0; 75 task->wakeup_inner(this); 76 did_rendezvous = true; 77 } 78 } 79 } 80 81 if (!did_rendezvous) { 82 // If the task wasn't waiting specifically on this port, 83 // it may be waiting on a group of ports 84 85 rust_port_selector *port_selector = task->get_port_selector(); 86 // The port selector will check if the task is blocked, not us. 87 port_selector->msg_sent_on(this); 88 } 89} 90 91void rust_port::receive(void *dptr, uintptr_t *yield) { 92 LOG(task, comm, "port: 0x%" PRIxPTR ", dptr: 0x%" PRIxPTR 93 ", size: 0x%" PRIxPTR, 94 (uintptr_t) this, (uintptr_t) dptr, unit_sz); 95 96 scoped_lock with(lock); 97 98 *yield = false; 99 100 if (buffer.is_empty() == false) { 101 buffer.dequeue(dptr); 102 LOG(task, comm, "<=== read data ==="); 103 return; 104 } 105 memset(dptr, 0, buffer.unit_sz); 106 107 // No data was buffered on any incoming channel, so block this task on 108 // the port. Remember the rendezvous location so that any sender task 109 // can write to it before waking up this task. 110 111 LOG(task, comm, "<=== waiting for rendezvous data ==="); 112 task->rendezvous_ptr = (uintptr_t*) dptr; 113 task->block(this, "waiting for rendezvous data"); 114 115 // Blocking the task might fail if the task has already been killed, but 116 // in the event of both failure and success the task needs to yield. On 117 // success, it yields and waits to be unblocked. On failure it yields and 118 // is then fails the task. 119 120 *yield = true; 121} 122 123size_t rust_port::size() { 124 scoped_lock with(lock); 125 return buffer.size(); 126} 127 128void rust_port::log_state() { 129 LOG(task, comm, 130 "port size: %d", 131 buffer.size()); 132} 133 134// 135// Local Variables: 136// mode: C++ 137// fill-column: 78; 138// indent-tabs-mode: nil 139// c-basic-offset: 4 140// buffer-file-coding-system: utf-8-unix 141// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; 142// End: 143//