summaryrefslogtreecommitdiff
path: root/libcr_ipc/chan.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcr_ipc/chan.c')
-rw-r--r--libcr_ipc/chan.c146
1 files changed, 120 insertions, 26 deletions
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c
index 7dd1132..b7ecfc8 100644
--- a/libcr_ipc/chan.c
+++ b/libcr_ipc/chan.c
@@ -4,37 +4,131 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <string.h> /* for memcpy() */
+
+#include <libcr/coroutine.h> /* for cid_t, cr_* */
+#include <libmisc/assert.h>
+#include <libmisc/rand.h>
+
+#define IMPLEMENTATION_FOR_LIBCR_IPC_CHAN_H YES
#include <libcr_ipc/chan.h>
-void _cr_chan_dequeue(void *_ch, size_t) {
- struct _cr_chan *ch = _ch;
- _cr_ipc_dll_pop_from_front(&ch->waiters);
-}
+struct _cr_select_waiter {
+ cid_t cid;
+ struct _cr_select_arg_list_node *arg_vec;
+ size_t arg_cnt;
-void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size) {
- assert(ch);
- assert(val_ptr);
+ size_t ret;
+};
- if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */
- /* Copy. */
- struct _cr_chan_waiter *front = _cr_ipc_dll_node_cast(struct _cr_chan_waiter, ch->waiters.front);
- if (self_typ == _CR_CHAN_SENDER)
- memcpy(front->val_ptr, val_ptr, val_size);
+enum cr_select_class {
+ CR_SELECT_CLASS_DEFAULT,
+ CR_SELECT_CLASS_BLOCKING,
+ CR_SELECT_CLASS_NONBLOCK,
+};
+
+static inline enum cr_select_class cr_select_getclass(struct _cr_select_arg *arg) {
+ switch (arg->op) {
+ case _CR_SELECT_OP_RECV:
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_SEND)
+ return CR_SELECT_CLASS_NONBLOCK;
+ else
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_SEND:
+ if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_RECV)
+ return CR_SELECT_CLASS_NONBLOCK;
else
- memcpy(val_ptr, front->val_ptr, val_size);
- cr_unpause(front->cid);
- front->dequeue(front->dequeue_arg1,
- front->dequeue_arg2);
+ return CR_SELECT_CLASS_BLOCKING;
+ case _CR_SELECT_OP_DEFAULT:
+ return CR_SELECT_CLASS_DEFAULT;
+ default:
+ assert_notreached("invalid arg->op");
+ }
+}
+
+size_t cr_select_v(size_t arg_cnt, struct _cr_select_arg_list_node arg_vec[]) {
+ size_t cnt_blocking = 0;
+ size_t cnt_nonblock = 0;
+ size_t cnt_default = 0;
+
+ assert(arg_cnt);
+ assert(arg_vec);
+ cr_assert_in_coroutine();
+
+ for (size_t i = 0; i < arg_cnt; i++) {
+ switch (cr_select_getclass(&arg_vec[i].val)) {
+ case CR_SELECT_CLASS_BLOCKING:
+ cnt_blocking++;
+ break;
+ case CR_SELECT_CLASS_NONBLOCK:
+ cnt_nonblock++;
+ break;
+ case CR_SELECT_CLASS_DEFAULT:
+ cnt_default++;
+ break;
+ }
+ }
+
+ if (cnt_nonblock) {
+ size_t choice_among_nonblock = rand_uint63n(cnt_nonblock);
+ size_t choice_among_all = arg_cnt;
+ for (size_t i = 0, seen = 0; i < choice_among_all; i++) {
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_NONBLOCK) {
+ if (seen == choice_among_nonblock)
+ choice_among_all = i;
+ seen++;
+ }
+ }
+ assert(choice_among_all < arg_cnt);
+
+ struct _cr_select_arg *this = &arg_vec[choice_among_all].val;
+ assert(this->ch->waiters.front);
+ struct _cr_select_arg *other = &this->ch->waiters.front->val;
+ assert(this->val_siz == other->val_siz);
+ assert(this->ch == other->ch);
+ switch (this->op) {
+ case _CR_SELECT_OP_SEND:
+ assert(other->op == _CR_SELECT_OP_RECV);
+ memcpy(other->val_ptr, this->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_RECV:
+ assert(other->op == _CR_SELECT_OP_SEND);
+ memcpy(this->val_ptr, other->val_ptr, this->val_siz);
+ break;
+ case _CR_SELECT_OP_DEFAULT:
+ assert_notreached("_CR_SELECT_OP_DEFAULT is not CR_SELECT_CLASS_NONBLOCK");
+ }
+ struct _cr_select_waiter *waiter = other->waiter;
+ for (size_t i = 0; i < waiter->arg_cnt; i++) {
+ waiter->arg_vec[i].val.ch->nwaiters--;
+ dlist_remove(&waiter->arg_vec[i].val.ch->waiters, &waiter->arg_vec[i]);
+ if (&waiter->arg_vec[i].val == other)
+ waiter->ret = i;
+ }
+ cr_unpause(waiter->cid);
cr_yield();
- } else { /* blocking slow-path */
- struct _cr_chan_waiter self = {
- .cid = cr_getcid(),
- .val_ptr = val_ptr,
- .dequeue = _cr_chan_dequeue,
- .dequeue_arg1 = ch,
- };
- _cr_ipc_dll_push_to_rear(&ch->waiters, &self);
- ch->waiter_typ = self_typ;
- cr_pause_and_yield();
+ return choice_among_all;
+ }
+
+ if (cnt_default) {
+ for (size_t i = 0; i < arg_cnt; i++)
+ if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_DEFAULT)
+ return i;
+ assert_notreached("should have returned from inside for() loop");
+ }
+
+ assert(cnt_blocking && cnt_blocking == arg_cnt);
+
+ struct _cr_select_waiter waiter = {
+ .cid = cr_getcid(),
+ .arg_vec = arg_vec,
+ .arg_cnt = arg_cnt,
+ };
+ for (size_t i = 0; i < arg_cnt; i++) {
+ arg_vec[i].val.waiter = &waiter;
+ arg_vec[i].val.ch->nwaiters++;
+ dlist_push_to_rear(&arg_vec[i].val.ch->waiters, &arg_vec[i]);
}
+ cr_pause_and_yield();
+ return waiter.ret;
}