diff options
Diffstat (limited to 'libcr_ipc/rwmutex.c')
-rw-r--r-- | libcr_ipc/rwmutex.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/libcr_ipc/rwmutex.c b/libcr_ipc/rwmutex.c new file mode 100644 index 0000000..191b7fe --- /dev/null +++ b/libcr_ipc/rwmutex.c @@ -0,0 +1,98 @@ +/* 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_* */ +#include <libmisc/assert.h> + +#define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES +#include <libcr_ipc/rwmutex.h> + +struct cr_rwmutex_waiter { + bool is_reader; + cid_t cid; +}; +SLIST_DECLARE_NODE(_cr_rwmutex_waiter_list, struct cr_rwmutex_waiter); + +void cr_rwmutex_lock(cr_rwmutex_t *mu) { + assert(mu); + cr_assert_in_coroutine(); + + struct _cr_rwmutex_waiter_list_node self = { .val = { + .is_reader = false, + .cid = cr_getcid(), + }}; + slist_push_to_rear(&mu->waiters, &self); + if (mu->waiters.front != &self || mu->locked) + cr_pause_and_yield(); + assert(mu->waiters.front == &self); + + /* We now hold the lock (and are mu->waiters.front). */ + slist_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_list_node self = { .val = { + .is_reader = true, + .cid = cr_getcid(), + }}; + slist_push_to_rear(&mu->waiters, &self); + if (mu->waiters.front != &self || (mu->locked && mu->nreaders == 0)) + cr_pause_and_yield(); + assert(mu->waiters.front == &self); + + /* We now hold the lock (and are mu->waiters.front). */ + slist_pop_from_front(&mu->waiters); + mu->nreaders++; + mu->locked = true; + struct _cr_rwmutex_waiter_list_node *waiter = mu->waiters.front; + if (waiter && waiter->val.is_reader) { + assert(mu->unpausing); + cr_unpause(waiter->val.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_list_node *waiter = mu->waiters.front; + mu->unpausing = true; + cr_unpause(waiter->val.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_list_node *waiter = mu->waiters.front; + assert(!waiter->val.is_reader); + mu->unpausing = true; + cr_unpause(waiter->val.cid); + } else { + mu->locked = false; + } + } +} |