diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-01 17:28:29 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-01 17:28:29 -0700 |
commit | 188ac62a0c0f5519f5d45519fa7d224cb25305c6 (patch) | |
tree | 5543f56fc89c548cf411f76e317894005a6b198c /libcr_ipc/select.c | |
parent | f929205572e1d56fa3719b426b45aafded58e86a (diff) | |
parent | 18ad76a7a2baa9c78e01e85886f693e572fcb2a9 (diff) |
Merge branch 'lukeshu/misc'
Diffstat (limited to 'libcr_ipc/select.c')
-rw-r--r-- | libcr_ipc/select.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/libcr_ipc/select.c b/libcr_ipc/select.c new file mode 100644 index 0000000..4bd9067 --- /dev/null +++ b/libcr_ipc/select.c @@ -0,0 +1,102 @@ +/* libcr_ipc/select.c - Select between channels + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libcr_ipc/select.h> + +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) + 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) + 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"); + } +} + +void _cr_select_dequeue(void *_waiters, size_t idx) { + struct _cr_select_waiters *waiters = _waiters; + for (size_t i = 0; i < waiters->cnt; i++) + _cr_ipc_dll_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 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])) { + 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 = 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; + } + seen++; + } + } + assert_notreached("should have returned from inside for() loop"); + } + + if (cnt_default) { + for (size_t i = 0; i < arg_cnt; i++) + if (_cr_select_getclass(arg_vec[i]) == _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 = alloca(sizeof(struct _cr_chan_waiter) * arg_cnt), + }; + for (size_t i = 0; i < arg_cnt; i++) { + waiters.nodes[i] = (struct _cr_chan_waiter){ + .cid = cr_getcid(), + .val_ptr = arg_vec[i].val_ptr, + .dequeue = _cr_select_dequeue, + .dequeue_arg1 = &waiters, + .dequeue_arg2 = i, + }; + _cr_ipc_dll_push_to_rear(&arg_vec[i].ch->waiters, &waiters.nodes[i]); + } + cr_pause_and_yield(); + return waiters.cnt; +} |