From 902a7661cfed71b1f50aa95bac9345883cb435cc Mon Sep 17 00:00:00 2001 From: "Luke T. Shumaker" Date: Fri, 28 Feb 2025 20:15:16 -0700 Subject: Fix linker confusion --- libcr_ipc/select.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 libcr_ipc/select.c (limited to 'libcr_ipc/select.c') 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 + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include + +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; +} -- cgit v1.2.3-2-g168b