summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-09 07:47:30 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-09 12:24:29 -0700
commit648b8fe13166f44ba7d36a5b0d753386253155ff (patch)
treecb0f8e10da475f8a9765a7c0a8684d180eb0bb37
parent96a751d8a5d20b2acea5ae8d10ac3d051466c2c3 (diff)
libcr_ipc: Add cr_owned_mutexlukeshu/owned-mutex
-rw-r--r--libcr_ipc/CMakeLists.txt1
-rw-r--r--libcr_ipc/include/libcr_ipc/owned_mutex.h79
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_ */