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:29 -0700 |
commit | 648b8fe13166f44ba7d36a5b0d753386253155ff (patch) | |
tree | cb0f8e10da475f8a9765a7c0a8684d180eb0bb37 | |
parent | 96a751d8a5d20b2acea5ae8d10ac3d051466c2c3 (diff) |
libcr_ipc: Add cr_owned_mutexlukeshu/owned-mutex
-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_ */ |