diff options
Diffstat (limited to 'libcr_ipc/rwmutex.c')
-rw-r--r-- | libcr_ipc/rwmutex.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/libcr_ipc/rwmutex.c b/libcr_ipc/rwmutex.c new file mode 100644 index 0000000..4c5da81 --- /dev/null +++ b/libcr_ipc/rwmutex.c @@ -0,0 +1,100 @@ +/* libcr_ipc/rwmutex.c - Simple read/write mutexes for libcr + * + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <libcr/coroutine.h> /* for cid_t, cr_* */ + +#define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES +#include <libcr_ipc/rwmutex.h> + +struct cr_rwmutex_waiter { + lm_sll_node; + bool is_reader; + cid_t cid; +}; + +void cr_rwmutex_lock(cr_rwmutex_t *mu) { + assert(mu); + cr_assert_in_coroutine(); + + struct cr_rwmutex_waiter self = { + .is_reader = false, + .cid = cr_getcid(), + }; + lm_sll_push_to_rear(&mu->waiters, &self); + if (mu->waiters.front != &self.lm_sll_node || mu->locked) + cr_pause_and_yield(); + assert(mu->waiters.front == &self.lm_sll_node); + + /* We now hold the lock (and are mu->waiters.front). */ + lm_sll_pop_from_front(&mu->waiters); + assert(mu->nreaders == 0); + mu->locked = true; + mu->unpausing = false; +} + +void cr_rwmutex_rlock(cr_rwmutex_t *mu) { + assert(mu); + cr_assert_in_coroutine(); + + struct cr_rwmutex_waiter self = { + .is_reader = true, + .cid = cr_getcid(), + }; + lm_sll_push_to_rear(&mu->waiters, &self); + if (mu->waiters.front != &self.lm_sll_node || (mu->locked && mu->nreaders == 0)) + cr_pause_and_yield(); + assert(mu->waiters.front == &self.lm_sll_node); + + /* We now hold the lock (and are mu->waiters.front). */ + lm_sll_pop_from_front(&mu->waiters); + mu->nreaders++; + mu->locked = true; + struct cr_rwmutex_waiter *waiter = + lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); + if (waiter && waiter->is_reader) { + assert(mu->unpausing); + cr_unpause(waiter->cid); + } else { + mu->unpausing = false; + } +} + +void cr_rwmutex_unlock(cr_rwmutex_t *mu) { + assert(mu); + cr_assert_in_coroutine(); + + assert(mu->locked); + assert(mu->nreaders == 0); + assert(!mu->unpausing); + if (mu->waiters.front) { + struct cr_rwmutex_waiter *waiter = + lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); + mu->unpausing = true; + cr_unpause(waiter->cid); + } else { + mu->locked = false; + } +} + +void cr_rwmutex_runlock(cr_rwmutex_t *mu) { + assert(mu); + cr_assert_in_coroutine(); + + assert(mu->locked); + assert(mu->nreaders > 0); + mu->nreaders--; + if (mu->nreaders == 0 && !mu->unpausing) { + if (mu->waiters.front) { + struct cr_rwmutex_waiter *waiter = + lm_sll_node_cast(struct cr_rwmutex_waiter, mu->waiters.front); + assert(!waiter->is_reader); + mu->unpausing = true; + cr_unpause(waiter->cid); + } else { + mu->locked = false; + } + } +} |