/* 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_* */ #include #define IMPLEMENTATION_FOR_LIBCR_IPC_RWMUTEX_H YES #include 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; } } }