diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-12-09 07:47:30 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-12-09 12:24:43 -0700 |
commit | f9d571d233711c4a1a65c4d5756e09bc9960cc0a (patch) | |
tree | f83ed78fd5169355121895b2e10870dfa9ac798a /libcr_ipc | |
parent | fe93ddf1556c7e03946a4f8cb964cc782e7d7a6c (diff) |
libcr_ipc: Add cr_owned_mutex
Diffstat (limited to 'libcr_ipc')
-rw-r--r-- | libcr_ipc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libcr_ipc/include/libcr_ipc/owned_mutex.h | 79 |
2 files changed, 80 insertions, 0 deletions
diff --git a/libcr_ipc/CMakeLists.txt b/libcr_ipc/CMakeLists.txt index 2a1c747..3ab56ab 100644 --- a/libcr_ipc/CMakeLists.txt +++ b/libcr_ipc/CMakeLists.txt @@ -13,6 +13,7 @@ set(ipc_tests chan #select #mutex + #owned_mutex #rpc #sema ) diff --git a/libcr_ipc/include/libcr_ipc/owned_mutex.h b/libcr_ipc/include/libcr_ipc/owned_mutex.h new file mode 100644 index 0000000..c66240f --- /dev/null +++ b/libcr_ipc/include/libcr_ipc/owned_mutex.h @@ -0,0 +1,79 @@ +/* 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_ */ |