diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-07 20:29:35 -0600 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-04-07 20:29:35 -0600 |
commit | d053a0f6dffc575611324b81acaef0d1554bda6c (patch) | |
tree | 3eb702a06625ef1f6be0648f7fec311b628808e9 /libcr_ipc/include | |
parent | 33b7cf675238367db2fc0528dea103d7792eaa0e (diff) | |
parent | 062e063f76c5937be4dc8465f6471ff9b5eb7be9 (diff) |
Merge branch 'lukeshu/ipc'
Diffstat (limited to 'libcr_ipc/include')
-rw-r--r-- | libcr_ipc/include/libcr_ipc/_linkedlist.h | 99 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/_linkedlist_pub.h | 26 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/chan.h | 85 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/mutex.h | 41 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/owned_mutex.h | 79 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/rpc.h | 89 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/rwmutex.h | 77 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/select.h | 85 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/sema.h | 60 |
9 files changed, 190 insertions, 451 deletions
diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist.h b/libcr_ipc/include/libcr_ipc/_linkedlist.h deleted file mode 100644 index 543e058..0000000 --- a/libcr_ipc/include/libcr_ipc/_linkedlist.h +++ /dev/null @@ -1,99 +0,0 @@ -/* libcr_ipc/_linkedlist.h - Common low-level linked lists for use in libcr_ipc - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBCR_IPC__LINKEDLIST_H_ -#define _LIBCR_IPC__LINKEDLIST_H_ - -#include <libmisc/assert.h> - -/* singly linked list *********************************************************/ - -typedef struct __cr_ipc_sll_node { - struct __cr_ipc_sll_node *rear; -} _cr_ipc_sll_node; - -typedef struct { - _cr_ipc_sll_node *front, *rear; -} _cr_ipc_sll_root; - -#define _cr_ipc_sll_node_cast(node_typ, node_ptr) \ - ({ \ - static_assert(_Generic(node_ptr, _cr_ipc_sll_node *: 1, default: 0), \ - "typeof("#node_ptr") != _cr_ipc_sll_node *"); \ - assert(node_ptr); \ - static_assert(offsetof(node_typ, _cr_ipc_sll_node) == 0); \ - ((node_typ*)(node_ptr)); \ - }) - -static inline void _cr_ipc_sll_push_to_rear(_cr_ipc_sll_root *root, _cr_ipc_sll_node *node) { - assert(root); - node->rear = NULL; - if (root->rear) - root->rear->rear = node; - else - root->front = node; - root->rear = node; -} - -static inline void _cr_ipc_sll_pop_from_front(_cr_ipc_sll_root *root) { - assert(root); - assert(root->front); - root->front = root->front->rear; - if (!root->front) - root->rear = NULL; -} - -/* doubly linked list *********************************************************/ - -typedef struct __cr_ipc_dll_node { - struct __cr_ipc_dll_node *front, *rear; -} _cr_ipc_dll_node; - -typedef struct { - _cr_ipc_dll_node *front, *rear; -} _cr_ipc_dll_root; - -#define _cr_ipc_dll_node_cast(node_typ, node_ptr) \ - ({ \ - static_assert(_Generic(node_ptr, _cr_ipc_dll_node *: 1, default: 0), \ - "typeof("#node_ptr") != _cr_ipc_dll_node *"); \ - assert(node_ptr); \ - static_assert(offsetof(node_typ, _cr_ipc_dll_node) == 0); \ - ((node_typ*)(node_ptr)); \ - }) - -static inline void _cr_ipc_dll_push_to_rear(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) { - assert(root); - assert(node); - node->front = root->rear; - node->rear = NULL; - if (root->rear) - root->rear->rear = node; - else - root->front = node; - root->rear = node; -} - -static inline void _cr_ipc_dll_remove(_cr_ipc_dll_root *root, _cr_ipc_dll_node *node) { - assert(root); - assert(node); - if (node->front) - node->front->rear = node->rear; - else - root->front = node->rear; - if (node->rear) - node->rear->front = node->front; - else - root->rear = node->front; -} - -static inline void _cr_ipc_dll_pop_from_front(_cr_ipc_dll_root *root) { - assert(root); - assert(root->front); - _cr_ipc_dll_remove(root, root->front); -} - -#endif /* _LIBCR_IPC__LINKEDLIST_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h b/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h new file mode 100644 index 0000000..6719ba4 --- /dev/null +++ b/libcr_ipc/include/libcr_ipc/_linkedlist_pub.h @@ -0,0 +1,26 @@ +/* libcr_ipc/_linkedlist_pub.h - Common low-level linked lists for use in libcr_ipc + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBCR_IPC__LINKEDLIST_PUB_H_ +#define _LIBCR_IPC__LINKEDLIST_PUB_H_ + +/* singly linked list *********************************************************/ + +struct _cr_ipc_sll_node; + +typedef struct { + struct _cr_ipc_sll_node *front, *rear; +} _cr_ipc_sll_root; + +/* doubly linked list *********************************************************/ + +struct _cr_ipc_dll_node; + +typedef struct { + struct _cr_ipc_dll_node *front, *rear; +} _cr_ipc_dll_root; + +#endif /* _LIBCR_IPC__LINKEDLIST_PUB_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/chan.h b/libcr_ipc/include/libcr_ipc/chan.h index dafc92d..ad311a0 100644 --- a/libcr_ipc/include/libcr_ipc/chan.h +++ b/libcr_ipc/include/libcr_ipc/chan.h @@ -9,11 +9,12 @@ #include <stdbool.h> /* for bool */ #include <stddef.h> /* for size_t */ -#include <string.h> /* for memcpy */ -#include <libcr/coroutine.h> /* for cid_t, cr_* */ +#include <libmisc/macro.h> /* LM_CAT2_() */ -#include <libcr_ipc/_linkedlist.h> +#include <libcr_ipc/_linkedlist_pub.h> + +/* base channels **************************************************************/ /** * CR_CHAN_DECLARE(NAME, VAL_T) declares the following type and @@ -78,51 +79,97 @@ struct _cr_chan core; \ VAL_T vals[0]; \ } NAME##_t; \ - \ + \ static inline void NAME##_send(NAME##_t *ch, VAL_T val) { \ cr_assert_in_coroutine(); \ _cr_chan_xfer(_CR_CHAN_SENDER, &ch->core, &val, sizeof(val)); \ } \ - \ + \ static inline VAL_T NAME##_recv(NAME##_t *ch) { \ cr_assert_in_coroutine(); \ VAL_T val; \ _cr_chan_xfer(_CR_CHAN_RECVER, &ch->core, &val, sizeof(val)); \ return val; \ } \ - \ + \ static inline bool NAME##_can_send(NAME##_t *ch) { \ cr_assert_in_coroutine(); \ return ch->core.waiters.front && \ ch->core.waiter_typ == _CR_CHAN_RECVER; \ } \ - \ + \ static inline bool NAME##_can_recv(NAME##_t *ch) { \ cr_assert_in_coroutine(); \ return ch->core.waiters.front && \ ch->core.waiter_typ == _CR_CHAN_SENDER; \ - } + } \ + \ + extern int LM_CAT2_(_CR_CHAN_FORCE_SEMICOLON_, __COUNTER__) enum _cr_chan_waiter_typ { _CR_CHAN_SENDER, _CR_CHAN_RECVER, }; -struct _cr_chan_waiter { - _cr_ipc_dll_node; - cid_t cid; - void *val_ptr; - void (*dequeue)(void *, size_t); - void *dequeue_arg1; - size_t dequeue_arg2; -}; - struct _cr_chan { enum _cr_chan_waiter_typ waiter_typ; - _cr_ipc_dll_root waiters; + _cr_ipc_dll_root waiters; }; -void _cr_chan_dequeue(void *_ch, size_t); 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 { + 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; +}; + +#define CR_SELECT_RECV(CH, VALP) \ + /* The _valp temporary variable is to get the compiler to check that \ + * the types are compatible. */ \ + ((struct cr_select_arg){ \ + .op = _CR_SELECT_OP_RECV, \ + .ch = &((CH)->core), \ + .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \ + .val_siz = sizeof((CH)->vals[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) \ + /* The _valp temporary variable is to get the compiler to check that \ + * the types are compatible. */ \ + ((struct cr_select_arg){ \ + .op = _CR_SELECT_OP_SEND, \ + .ch = &((CH)->core), \ + .val_ptr = ({ typeof((CH)->vals[0]) *_valp = VALP; _valp; }), \ + .val_siz = sizeof((CH)->vals[0]), \ + }) +#define CR_SELECT_DEFAULT \ + ((struct cr_select_arg){ \ + .op = _CR_SELECT_OP_DEFAULT, \ + }) + +/* cr_select_v(arg_cnt, arg_vec) **********************************************/ + +size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]); + +/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/ + +#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])); \ +}) + #endif /* _LIBCR_IPC_CHAN_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/mutex.h b/libcr_ipc/include/libcr_ipc/mutex.h index cba8c19..ec40f5c 100644 --- a/libcr_ipc/include/libcr_ipc/mutex.h +++ b/libcr_ipc/include/libcr_ipc/mutex.h @@ -1,6 +1,6 @@ /* libcr_ipc/mutex.h - Simple mutexes for libcr * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -9,14 +9,9 @@ #include <stdbool.h> /* for bool */ -#include <libcr/coroutine.h> /* for cid_t, cr_* */ +#include <libmisc/private.h> -#include <libcr_ipc/_linkedlist.h> - -struct _cr_mutex_waiter { - _cr_ipc_sll_node; - cid_t cid; -}; +#include <libcr_ipc/_linkedlist_pub.h> /** * A cr_mutex_t is a fair mutex. @@ -27,8 +22,10 @@ struct _cr_mutex_waiter { * first place, then it has no business unlocking one. */ typedef struct { + BEGIN_PRIVATE(LIBCR_IPC_MUTEX_H); bool locked; _cr_ipc_sll_root waiters; + END_PRIVATE(LIBCR_IPC_MUTEX_H); } cr_mutex_t; /** @@ -38,21 +35,7 @@ typedef struct { * @cr_pauses maybe * @cr_yields maybe */ -static inline void cr_mutex_lock(cr_mutex_t *mu) { - assert(mu); - cr_assert_in_coroutine(); - - if (!mu->locked) /* non-blocking fast-path */ - mu->locked = true; - else { /* blocking slow-path */ - struct _cr_mutex_waiter self = { - .cid = cr_getcid(), - }; - _cr_ipc_sll_push_to_rear(&mu->waiters, &self); - cr_pause_and_yield(); - } - assert(mu->locked); -} +void cr_mutex_lock(cr_mutex_t *mu); /** * Unlock the mutex. Unblocks a coroutine that is blocked on @@ -62,16 +45,6 @@ static inline void cr_mutex_lock(cr_mutex_t *mu) { * @cr_pauses never * @cr_yields never */ -static inline void cr_mutex_unlock(cr_mutex_t *mu) { - assert(mu); - cr_assert_in_coroutine(); - - assert(mu->locked); - if (mu->waiters.front) { - cr_unpause(_cr_ipc_sll_node_cast(struct _cr_mutex_waiter, mu->waiters.front)->cid); - _cr_ipc_sll_pop_from_front(&mu->waiters); - } else - mu->locked = false; -} +void cr_mutex_unlock(cr_mutex_t *mu); #endif /* _LIBCR_IPC_MUTEX_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/owned_mutex.h b/libcr_ipc/include/libcr_ipc/owned_mutex.h deleted file mode 100644 index c66240f..0000000 --- a/libcr_ipc/include/libcr_ipc/owned_mutex.h +++ /dev/null @@ -1,79 +0,0 @@ -/* libcr_ipc/owned_mutex.h - Owned mutexes for libcr - * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#ifndef _LIBCR_IPC_OWNED_MUTEX_H_ -#define _LIBCR_IPC_OWNED_MUTEX_H_ - -#include <stdbool.h> /* for bool */ - -#include <libcr/coroutine.h> /* for cid_t, cr_unpause(), cr_pause_and_yield() */ - -#include <libcr_ipc/_linkedlist.h> - -struct _cr_owned_mutex_waiter { - _cr_ipc_sll_node; - cid_t cid; -}; - -/** - * A cr_owned_mutex_t is a fair mutex that tracks not just whether it - * is locked, but which coroutine has it locked. - * - * None of the methods have `_from_intrhandler` variants because (1) - * an interrupt handler can't block, so it shouldn't ever lock a mutex - * because that can block; and (2) if it can't lock a mutex in the - * first place, then it has no business unlocking one. - */ -typedef struct { - cid_t owner; - _cr_ipc_sll_root waiters; -} cr_owned_mutex_t; - -/** - * Lock the mutex. Blocks if it is already locked. - * - * @runs_in coroutine - * @cr_pauses maybe - * @cr_yields maybe - */ -static inline void cr_owned_mutex_lock(cr_owned_mutex_t *mu) { - assert(mu); - - if (!mu->owner) /* non-blocking fast-path */ - mu->owner = cr_getcid(); - else { /* blocking slow-path */ - struct _cr_owned_mutex_waiter self = { - .cid = cr_getcid(), - }; - _cr_ipc_sll_push_to_rear(&mu->waiters, &self); - cr_pause_and_yield(); - } - assert(mu->owner == cr_getcid()); -} - -/** - * Unlock the mutex. Unblocks a coroutine that is blocked on - * cr_owned_mutex_lock(). Must be the called from the same coroutine - * that called cr_owned_mutex_lock(). - * - * @runs_in coroutine - * @cr_pauses never - * @cr_yields never - */ -static inline void cr_owned_mutex_unlock(cr_owned_mutex_t *mu) { - assert(mu); - - assert(mu->owner == cr_getcid()); - if (mu->waiters.front) { - cid_t waiter = _cr_ipc_sll_node_cast(struct _cr_owned_mutex_waiter, mu->waiters.front)->cid; - cr_unpause(waiter); - mu->owner = waiter; - _cr_ipc_sll_pop_from_front(&mu->waiters); - } else - mu->owner = 0; -} - -#endif /* _LIBCR_IPC_OWNED_MUTEX_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/rpc.h b/libcr_ipc/include/libcr_ipc/rpc.h index 0600399..07ace95 100644 --- a/libcr_ipc/include/libcr_ipc/rpc.h +++ b/libcr_ipc/include/libcr_ipc/rpc.h @@ -8,11 +8,10 @@ #define _LIBCR_IPC_RPC_H_ #include <stdbool.h> /* for bool */ -#include <string.h> /* for memcpy() */ -#include <libcr/coroutine.h> /* for cid_t, cr_* */ +#include <libmisc/macro.h> /* for LM_CAT2_() */ -#include <libcr_ipc/_linkedlist.h> +#include <libcr_ipc/_linkedlist_pub.h> /** * CR_RPC_DECLARE(NAME, REQ_T, RESP_T) declares the following types @@ -136,95 +135,21 @@ *(req._resp) = resp; \ cr_unpause(req._requester); \ cr_yield(); \ - } + } \ + \ + extern int LM_CAT2_(_CR_RPC_FORCE_SEMICOLON_, __COUNTER__) enum _cr_rpc_waiter_typ { _CR_RPC_REQUESTER, _CR_RPC_RESPONDER, }; -struct _cr_rpc_requester { - _cr_ipc_sll_node; - cid_t cid; - void *req_ptr; /* where to read req from */ - void *resp_ptr; /* where to write resp to */ -}; - -struct _cr_rpc_responder { - _cr_ipc_sll_node; - /* before enqueued | after dequeued */ - /* -------------------+-------------------- */ - cid_t cid; /* responder cid | requester cid */ - void *ptr; /* where to write req | where to write resp */ -}; - struct _cr_rpc { enum _cr_rpc_waiter_typ waiter_typ; _cr_ipc_sll_root waiters; }; -static inline void _cr_rpc_send_req(struct _cr_rpc *ch, - void *req_ptr, size_t req_size, - void *resp_ptr) -{ - assert(ch); - assert(req_ptr); - assert(resp_ptr); - - if (ch->waiters.front && ch->waiter_typ != _CR_RPC_REQUESTER) { /* fast-path (still blocks) */ - struct _cr_rpc_responder *responder = - _cr_ipc_sll_node_cast(struct _cr_rpc_responder, ch->waiters.front); - _cr_ipc_sll_pop_from_front(&ch->waiters); - /* Copy the req to the responder's stack. */ - memcpy(responder->ptr, req_ptr, req_size); - /* Notify the responder that we have done so. */ - cr_unpause(responder->cid); - responder->cid = cr_getcid(); - responder->ptr = resp_ptr; - /* Wait for the responder to set `*resp_ptr`. */ - cr_pause_and_yield(); - } else { /* blocking slow-path */ - struct _cr_rpc_requester self = { - .cid = cr_getcid(), - .req_ptr = req_ptr, - .resp_ptr = resp_ptr, - }; - _cr_ipc_sll_push_to_rear(&ch->waiters, &self); - /* Wait for a responder to both copy our req and sed - * `*resp_ptr`. */ - cr_pause_and_yield(); - } -} - -static inline void _cr_rpc_recv_req(struct _cr_rpc *ch, - void *req_ptr, size_t req_size, - void **ret_resp_ptr, - cid_t *ret_requester) -{ - assert(ch); - assert(req_ptr); - assert(ret_resp_ptr); - assert(ret_requester); - - if (ch->waiters.front && ch->waiter_typ != _CR_RPC_RESPONDER) { /* non-blocking fast-path */ - struct _cr_rpc_requester *requester = - _cr_ipc_sll_node_cast(struct _cr_rpc_requester, ch->waiters.front); - _cr_ipc_sll_pop_from_front(&ch->waiters); - - memcpy(req_ptr, requester->req_ptr, req_size); - *ret_requester = requester->cid; - *ret_resp_ptr = requester->resp_ptr; - } else { /* blocking slow-path */ - struct _cr_rpc_responder self = { - .cid = cr_getcid(), - .ptr = req_ptr, - }; - _cr_ipc_sll_push_to_rear(&ch->waiters, &self); - ch->waiter_typ = _CR_RPC_RESPONDER; - cr_pause_and_yield(); - *ret_requester = self.cid; - *ret_resp_ptr = self.ptr; - } -} +void _cr_rpc_send_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void *resp_ptr); +void _cr_rpc_recv_req(struct _cr_rpc *ch, void *req_ptr, size_t req_size, void **ret_resp_ptr, cid_t *ret_requester); #endif /* _LIBCR_IPC_RPC_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/rwmutex.h b/libcr_ipc/include/libcr_ipc/rwmutex.h new file mode 100644 index 0000000..924acce --- /dev/null +++ b/libcr_ipc/include/libcr_ipc/rwmutex.h @@ -0,0 +1,77 @@ +/* libcr_ipc/rwmutex.h - Simple read/write mutexes for libcr + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBCR_IPC_RWMUTEX_H_ +#define _LIBCR_IPC_RWMUTEX_H_ + +#include <stdbool.h> + +#include <libmisc/private.h> + +#include <libcr_ipc/_linkedlist_pub.h> + +/** + * A cr_rwmutex_t is a fair read/write mutex. + * + * A waiting writer blocks any new readers; this ensures that the + * writer is able to eventually get the lock. + * + * None of the methods have `_from_intrhandler` variants because (1) + * an interrupt handler can't block, so it shouldn't ever lock a mutex + * because that can block; and (2) if it can't lock a mutex in the + * first place, then it has no business unlocking one. + */ +typedef struct { + BEGIN_PRIVATE(LIBCR_IPC_RWMUTEX_H); + unsigned nreaders; + bool locked; + bool unpausing; + _cr_ipc_sll_root waiters; + END_PRIVATE(LIBCR_IPC_RWMUTEX_H); +} cr_rwmutex_t; + +/** + * Lock the mutex for writing. Blocks if it is already locked. + * + * @runs_in coroutine + * @cr_pauses maybe + * @cr_yields maybe + */ +void cr_rwmutex_lock(cr_rwmutex_t *mu); + +/** + * Undo a single cr_rwmutex_lock() call. Unblocks either a single + * coroutine that is blocked on cr_rwmutex_lock() or arbitrarily many + * coroutines that are blocked on cr_rwmutex_rlock(). + * + * @runs_in coroutine + * @cr_pauses never + * @cr_yields never + */ +void cr_rwmutex_unlock(cr_rwmutex_t *mu); + +/** + * Lock the mutex for reading. Blocks if it is already locked for + * writing. + * + * @runs_in coroutine + * @cr_pauses maybe + * @cr_yields maybe + */ +void cr_rwmutex_rlock(cr_rwmutex_t *mu); + +/** + * Undo a single cr_rwmutext_rock() call. If the reader count is + * reduced to zero, unblocks a single coroutine that is blocked on + * cr_rwmutex_lock(). + * + * @runs_in coroutine + * @cr_pauses never + * @cr_yields never + */ +void cr_rwmutex_runlock(cr_rwmutex_t *mu); + +#endif /* _LIBCR_IPC_RWMUTEX_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h deleted file mode 100644 index 0e35351..0000000 --- a/libcr_ipc/include/libcr_ipc/select.h +++ /dev/null @@ -1,85 +0,0 @@ -/* libcr_ipc/select.h - Select between channels - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#include <alloca.h> /* for alloca() */ -#include <stddef.h> /* for size_t */ - -#include <libmisc/assert.h> -#include <libmisc/rand.h> - -#include <libcr_ipc/chan.h> - -#ifndef _LIBCR_IPC_SELECT_H_ -#define _LIBCR_IPC_SELECT_H_ - -/* arguments ******************************************************************/ - -/** - * Do not populate cr_select_arg yourself; use the - * CR_SELECT_{RECV,SEND,DEFAULT} macros. - */ -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; -}; - -#define CR_SELECT_RECV(CH, VALP) ({ \ - /* The _valp indirection is to get the \ - * compiler to check that the types are \ - * compatible. */ \ - typeof((CH)->vals[0]) *_valp = VALP; \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_RECV, \ - .ch = &((CH)->core), \ - .val_ptr = _valp, \ - .val_siz = sizeof((CH)->vals[0]), \ - }); \ - }) -#define CR_SELECT_SEND(CH, VAL) ({ \ - typeof((CH)->vals[0]) val_lvalue = VAL; \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_SEND, \ - .ch = &((CH)->core), \ - .val_ptr = &val_lvalue;, \ - .val_siz = sizeof((CH)->vals[0]), \ - }); \ - }) -#define CR_SELECT_DEFAULT \ - ((struct cr_select_arg){ \ - .op = _CR_SELECT_OP_DEFAULT, \ - }) - -/* cr_select_v(arg_cnt, arg_vec) **********************************************/ - -enum _cr_select_class { - _CR_SELECT_CLASS_DEFAULT, - _CR_SELECT_CLASS_BLOCKING, - _CR_SELECT_CLASS_NONBLOCK, -}; - -struct _cr_select_waiters { - size_t cnt; - struct cr_select_arg *args; - struct _cr_chan_waiter *nodes; -}; - -void _cr_select_dequeue(void *_waiters, size_t idx); -size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]); - -/* cr_select_l(arg1, arg2, arg3, ...) ******************************************/ - -#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])); \ -}) - -#endif /* _LIBCR_IPC_SELECT_H_ */ diff --git a/libcr_ipc/include/libcr_ipc/sema.h b/libcr_ipc/include/libcr_ipc/sema.h index fcabf28..ae8d93d 100644 --- a/libcr_ipc/include/libcr_ipc/sema.h +++ b/libcr_ipc/include/libcr_ipc/sema.h @@ -9,14 +9,9 @@ #include <stdbool.h> -#include <libcr/coroutine.h> /* for cid_t, cr_* */ +#include <libmisc/private.h> -#include <libcr_ipc/_linkedlist.h> - -struct _cr_sema_waiter { - _cr_ipc_sll_node; - cid_t cid; -}; +#include <libcr_ipc/_linkedlist_pub.h> /** * A cr_sema_t is a fair unbounded[1] counting semaphore. @@ -24,9 +19,11 @@ struct _cr_sema_waiter { * [1]: Well, UINT_MAX */ typedef struct { + BEGIN_PRIVATE(LIBCR_IPC_SEMA_H); unsigned int cnt; bool unpausing; _cr_ipc_sll_root waiters; + END_PRIVATE(LIBCR_IPC_SEMA_H); } cr_sema_t; /** @@ -36,36 +33,14 @@ typedef struct { * @cr_pauses never * @cr_yields never */ -static inline void cr_sema_signal(cr_sema_t *sema) { - assert(sema); - cr_assert_in_coroutine(); - - bool saved = cr_save_and_disable_interrupts(); - sema->cnt++; - if (sema->waiters.front && !sema->unpausing) { - cr_unpause( - _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid); - sema->unpausing = true; - } - cr_restore_interrupts(saved); -} +void cr_sema_signal(cr_sema_t *sema); /** * Like cr_sema_signal(), but for use from an interrupt handler. * * @runs_in intrhandler */ -static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) { - assert(sema); - cr_assert_in_intrhandler(); - - sema->cnt++; - if (sema->waiters.front && !sema->unpausing) { - cr_unpause_from_intrhandler( - _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid); - sema->unpausing = true; - } -} +void cr_sema_signal_from_intrhandler(cr_sema_t *sema); /** * Wait until the semaphore is >0, then decrement it. @@ -74,27 +49,6 @@ static inline void cr_sema_signal_from_intrhandler(cr_sema_t *sema) { * @cr_pauses maybe * @cr_yields maybe */ -static inline void cr_sema_wait(cr_sema_t *sema) { - assert(sema); - cr_assert_in_coroutine(); - - bool saved = cr_save_and_disable_interrupts(); - - struct _cr_sema_waiter self = { - .cid = cr_getcid(), - }; - _cr_ipc_sll_push_to_rear(&sema->waiters, &self); - if (sema->waiters.front != &self._cr_ipc_sll_node || !sema->cnt) - cr_pause_and_yield(); - assert(sema->waiters.front == &self._cr_ipc_sll_node && sema->cnt); - _cr_ipc_sll_pop_from_front(&sema->waiters); - sema->cnt--; - if (sema->cnt && sema->waiters.front) - cr_unpause( - _cr_ipc_sll_node_cast(struct _cr_sema_waiter, sema->waiters.front)->cid); - else - sema->unpausing = false; - cr_restore_interrupts(saved); -} +void cr_sema_wait(cr_sema_t *sema); #endif /* _LIBCR_IPC_SEMA_H_ */ |