/* libcr_ipc/rwmutex.c - Simple read/write mutexes for libcr * * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ #include /* for cid_t, cr_* */ #define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES #include 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; } } }