diff options
Diffstat (limited to 'libcr_ipc')
-rw-r--r-- | libcr_ipc/chan.c | 149 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/chan.h | 89 |
2 files changed, 103 insertions, 135 deletions
diff --git a/libcr_ipc/chan.c b/libcr_ipc/chan.c index f20b8a0..7708fe4 100644 --- a/libcr_ipc/chan.c +++ b/libcr_ipc/chan.c @@ -7,57 +7,19 @@ #include <string.h> /* for memcpy() */ #include <libcr/coroutine.h> /* for cid_t, cr_* */ -#include <libmisc/alloc.h> #include <libmisc/assert.h> #include <libmisc/rand.h> +#define IMPLEMENTATION_FOR_LIBCR_IPC_CHAN_H YES #include <libcr_ipc/chan.h> -/* base channels **************************************************************/ - -struct cr_chan_waiter { +struct _cr_select_waiter { cid_t cid; - void *val_ptr; - void (*dequeue)(void *, size_t); - void *dequeue_arg1; - size_t dequeue_arg2; -}; -DLIST_DECLARE_NODE(_cr_chan_waiter_list, struct cr_chan_waiter); - -void cr_chan_dequeue(void *_ch, size_t) { - struct _cr_chan *ch = _ch; - dlist_pop_from_front(&ch->waiters); -} - -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); + struct _cr_select_arg_list_node *arg_vec; + size_t arg_cnt; - if (ch->waiters.front && ch->waiter_typ != self_typ) { /* non-blocking fast-path */ - /* Copy. */ - struct _cr_chan_waiter_list_node *front = ch->waiters.front; - if (self_typ == _CR_CHAN_SENDER) - memcpy(front->val.val_ptr, val_ptr, val_size); - else - memcpy(val_ptr, front->val.val_ptr, val_size); - cr_unpause(front->val.cid); - front->val.dequeue(front->val.dequeue_arg1, - front->val.dequeue_arg2); - cr_yield(); - } else { /* blocking slow-path */ - struct _cr_chan_waiter_list_node self = { .val = { - .cid = cr_getcid(), - .val_ptr = val_ptr, - .dequeue = cr_chan_dequeue, - .dequeue_arg1 = ch, - }}; - dlist_push_to_rear(&ch->waiters, &self); - ch->waiter_typ = self_typ; - cr_pause_and_yield(); - } -} - -/* select *********************************************************************/ + size_t ret; +}; enum cr_select_class { CR_SELECT_CLASS_DEFAULT, @@ -65,40 +27,26 @@ enum cr_select_class { CR_SELECT_CLASS_NONBLOCK, }; -struct cr_select_waiters { - size_t cnt; - struct cr_select_arg *args; - struct _cr_chan_waiter_list_node *nodes; -}; - -static inline enum cr_select_class cr_select_getclass(struct cr_select_arg arg) { - switch (arg.op) { +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->waiter_typ == _CR_CHAN_SENDER) + 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->waiter_typ == _CR_CHAN_RECVER) + if (arg->ch->waiters.front && arg->ch->waiters.front->val.op == _CR_SELECT_OP_RECV) return CR_SELECT_CLASS_NONBLOCK; else return CR_SELECT_CLASS_BLOCKING; case _CR_SELECT_OP_DEFAULT: return CR_SELECT_CLASS_DEFAULT; default: - assert_notreached("invalid arg.op"); + assert_notreached("invalid arg->op"); } } -void cr_select_dequeue(void *_waiters, size_t idx) { - struct cr_select_waiters *waiters = _waiters; - for (size_t i = 0; i < waiters->cnt; i++) - dlist_remove(&(waiters->args[i].ch->waiters), - &(waiters->nodes[i])); - waiters->cnt = idx; -} - -size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { +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; @@ -108,7 +56,7 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { cr_assert_in_coroutine(); for (size_t i = 0; i < arg_cnt; i++) { - switch (cr_select_getclass(arg_vec[i])) { + switch (cr_select_getclass(&arg_vec[i].val)) { case CR_SELECT_CLASS_BLOCKING: cnt_blocking++; break; @@ -122,46 +70,63 @@ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { } if (cnt_nonblock) { - size_t choice = rand_uint63n(cnt_nonblock); - for (size_t i = 0, seen = 0; i < arg_cnt; i++) { - if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_NONBLOCK) { - if (seen == choice) { - _cr_chan_xfer(arg_vec[i].op == _CR_SELECT_OP_RECV - ? _CR_CHAN_RECVER - : _CR_CHAN_SENDER, - arg_vec[i].ch, - arg_vec[i].val_ptr, - arg_vec[i].val_siz); - return i; - } + 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_notreached("should have returned from inside for() loop"); + 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++) { + 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(); + return choice_among_all; } if (cnt_default) { for (size_t i = 0; i < arg_cnt; i++) - if (cr_select_getclass(arg_vec[i]) == CR_SELECT_CLASS_DEFAULT) + if (cr_select_getclass(&arg_vec[i].val) == CR_SELECT_CLASS_DEFAULT) return i; assert_notreached("should have returned from inside for() loop"); } - struct cr_select_waiters waiters = { - .cnt = arg_cnt, - .args = arg_vec, - .nodes = stack_alloc(arg_cnt, struct _cr_chan_waiter_list_node), + 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++) { - waiters.nodes[i] = (struct _cr_chan_waiter_list_node){ .val = { - .cid = cr_getcid(), - .val_ptr = arg_vec[i].val_ptr, - .dequeue = cr_select_dequeue, - .dequeue_arg1 = &waiters, - .dequeue_arg2 = i, - }}; - dlist_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]); + arg_vec[i].val.waiter = &waiter; + dlist_push_to_rear(&arg_vec[i].val.ch->waiters, &arg_vec[i]); } cr_pause_and_yield(); - return waiters.cnt; + return waiter.ret; } diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h index 83a81d4..79be220 100644 --- a/libcr_ipc/include/libcr_ipc/chan.h +++ b/libcr_ipc/include/libcr_ipc/chan.h @@ -11,6 +11,7 @@ #include <stddef.h> /* for size_t */ #include <libmisc/linkedlist.h> /* for DLIST_DECLARE() */ +#include <libmisc/private.h> /* base channels **************************************************************/ @@ -44,11 +45,9 @@ * * void cr_chan_send(NAME##_t *ch, VAL_T val); */ -#define cr_chan_send(CH, VAL) do { \ - cr_assert_in_coroutine(); \ - typeof((CH)->val_typ[0]) _val_lvalue = VAL; \ - _cr_chan_xfer(_CR_CHAN_SENDER, &(CH)->core, \ - &_val_lvalue, sizeof(_val_lvalue)); \ +#define cr_chan_send(CH, VAL) do { \ + typeof((CH)->val_typ[0]) _val_lvalue = VAL; \ + (void)cr_select_l(CR_SELECT_SEND(CH, &_val_lvalue)); \ } while(0) /** @@ -60,12 +59,10 @@ * * VAL_T cr_chan_recv(NAME##_T ch); */ -#define cr_chan_recv(CH) ({ \ - cr_assert_in_coroutine(); \ - typeof((CH)->val_typ[0]) _val_lvalue; \ - _cr_chan_xfer(_CR_CHAN_RECVER, &(CH)->core, \ - &_val_lvalue, sizeof(_val_lvalue)); \ - _val_lvalue; \ +#define cr_chan_recv(CH) ({ \ + typeof((CH)->val_typ[0]) _val_lvalue; \ + (void)cr_select_l(CR_SELECT_RECV(CH, &_val_lvalue)); \ + _val_lvalue; \ }) /** @@ -78,10 +75,10 @@ * * bool cr_chan_can_send(NAME##_t *ch); */ -#define cr_chan_can_send(CH) ({ \ - cr_assert_in_coroutine(); \ - (bool)((CH)->core.waiters.front && \ - (CH)->core.waiter_typ == _CR_CHAN_RECVER); \ +#define cr_chan_can_send(CH) ({ \ + cr_assert_in_coroutine(); \ + (bool)((CH)->core.waiters.front && \ + (CH)->core.waiters.front->val.op == _CR_SELECT_OP_RECV); \ }) /** @@ -94,75 +91,81 @@ * * bool cr_chan_can_recv(NAME##_t *ch); */ -#define cr_chan_can_recv(CH) ({ \ - cr_assert_in_coroutine(); \ - (bool)((CH)->core.waiters.front && \ - (CH)->core.waiter_typ == _CR_CHAN_SENDER); \ +#define cr_chan_can_recv(CH) ({ \ + cr_assert_in_coroutine(); \ + (bool)((CH)->core.waiters.front && \ + (CH)->core.waiters.front->val.op == _CR_SELECT_OP_SEND); \ }) - -enum _cr_chan_waiter_typ { - _CR_CHAN_SENDER, - _CR_CHAN_RECVER, -}; - -DLIST_DECLARE(_cr_chan_waiter_list); - +DLIST_DECLARE(_cr_select_arg_list); struct _cr_chan { - enum _cr_chan_waiter_typ waiter_typ; - struct _cr_chan_waiter_list waiters; + struct _cr_select_arg_list waiters; }; -void _cr_chan_xfer(enum _cr_chan_waiter_typ self_typ, struct _cr_chan *ch, void *val_ptr, size_t val_size); - /* cr_select arguments ********************************************************/ /** * Do not populate cr_select_arg yourself; use the * CR_SELECT_{RECV,SEND,DEFAULT} macros. */ -struct cr_select_arg { +struct _cr_select_waiter; +struct _cr_select_arg { enum { _CR_SELECT_OP_RECV, _CR_SELECT_OP_SEND, _CR_SELECT_OP_DEFAULT, - } op; - struct _cr_chan *ch; - void *val_ptr; - size_t val_siz; + } op; + struct _cr_chan *ch; + void *val_ptr; + size_t val_siz; + BEGIN_PRIVATE(LIBCR_IPC_CHAN_H); + struct _cr_select_waiter *waiter; + END_PRIVATE(LIBCR_IPC_CHAN_H); }; +DLIST_DECLARE_NODE(_cr_select_arg_list, struct _cr_select_arg); +#define cr_select_arg _cr_select_arg_list_node -#define CR_SELECT_RECV(CH, VALP) ((struct cr_select_arg){ \ +#define CR_SELECT_RECV(CH, VALP) ((struct cr_select_arg){ .val = { \ .op = _CR_SELECT_OP_RECV, \ .ch = &((CH)->core), \ /* The _valp temporary variable is to get the compiler to check that \ * the types are compatible. */ \ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \ .val_siz = sizeof((CH)->val_typ[0]), \ -}) +}}) /* BUG: It's bogus that CR_SELECT_SEND takes VALP instead of VAL, but * since we need an address, taking VAL would introduce uncomfortable * questions about where VAL sits on the stack. */ -#define CR_SELECT_SEND(CH, VALP) ((struct cr_select_arg){ \ +#define CR_SELECT_SEND(CH, VALP) ((struct cr_select_arg){ .val = { \ .op = _CR_SELECT_OP_SEND, \ .ch = &((CH)->core), \ /* The _valp temporary variable is to get the compiler to check that \ * the types are compatible. */ \ .val_ptr = ({ typeof((CH)->val_typ[0]) *_valp = VALP; _valp; }), \ .val_siz = sizeof((CH)->val_typ[0]), \ -}) +}}) -#define CR_SELECT_DEFAULT ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_DEFAULT, \ -}) +#define CR_SELECT_DEFAULT ((struct cr_select_arg){ .val = { \ + .op = _CR_SELECT_OP_DEFAULT, \ +}}) /* cr_select_v(arg_cnt, arg_vec) **********************************************/ +/** + * @runs_in coroutine + * @cr_pauses maybe + * @cr_yields always + */ size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]); /* cr_select_l(arg1, arg2, arg3, ...) ******************************************/ +/** + * @runs_in coroutine + * @cr_pauses maybe + * @cr_yields always + */ #define cr_select_l(...) ({ \ struct cr_select_arg _cr_select_args[] = { __VA_ARGS__ }; \ cr_select_v(sizeof(_cr_select_args)/sizeof(_cr_select_args[0]), \ |