summaryrefslogtreecommitdiff
path: root/na/kernel/sync/mutex.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:24:56 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:24:56 +0800
commit1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (patch)
tree0f5e01cd7c7a05abb6b1c7472d7f00f49caf02cc /na/kernel/sync/mutex.cc
parent63dc025705f6533b91fb1a7d26e6ed864efd9900 (diff)
downloadlume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.gz
lume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.xz
feat: establish bootable new architecture checkpoint
Replace the compressed boot path with a relocatable higher-half RISC-V entry, expand memory and SMP support, and add the driver, TTY, VFS, exec, signal, syscall, initramfs, and self-test infrastructure needed for a bootable kernel checkpoint.
Diffstat (limited to 'na/kernel/sync/mutex.cc')
-rw-r--r--na/kernel/sync/mutex.cc156
1 files changed, 156 insertions, 0 deletions
diff --git a/na/kernel/sync/mutex.cc b/na/kernel/sync/mutex.cc
new file mode 100644
index 0000000..c4d3e9d
--- /dev/null
+++ b/na/kernel/sync/mutex.cc
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+/*
+ * Mutex — Linux-style sleeping mutex (no PI).
+ *
+ * Implementation notes (mirroring kernel/locking/mutex.c, simplified):
+ *
+ * - Fast path (uncontended): wait_lock_ -> see owner_==null -> claim -> unlock.
+ * - Slow path (contended): enqueue MutexWaiter on stack, sleep, race for
+ * ownership after each wake. unlock() wakes only the head waiter.
+ * - No optimistic spin, no handoff anti-starvation, no interruptible wait.
+ * These belong to follow-up work (see mutex.md §5).
+ *
+ * Reference: docs/specs/mutex.md
+ */
+
+#include <lume/mutex.h>
+#include <lume/task.h>
+#include <lume/sched.h>
+#include <lume/klog.h>
+#include <arch/cpu.h>
+
+namespace {
+
+struct MutexWaiter {
+ list_node link;
+ TaskControlBlock* task;
+};
+
+} // namespace
+
+/* ------------------------------------------------------------------ */
+/* init */
+/* ------------------------------------------------------------------ */
+
+void Mutex::init(const char* name)
+{
+ owner_ = nullptr;
+ name_ = name;
+ wait_lock_.init(name != nullptr ? name : "mutex");
+ wait_list_.init();
+}
+
+/* ------------------------------------------------------------------ */
+/* try_lock — non-blocking, safe from any context */
+/* ------------------------------------------------------------------ */
+
+bool Mutex::try_lock()
+{
+ LockGuard g(wait_lock_);
+ if (owner_ != nullptr) {
+ return false;
+ }
+ owner_ = current_task();
+ return true;
+}
+
+/* ------------------------------------------------------------------ */
+/* lock — blocking, must be called in schedulable context */
+/* ------------------------------------------------------------------ */
+
+void Mutex::lock()
+{
+ TaskControlBlock* curr = current_task();
+
+ if (curr == nullptr) {
+ kernel_panic("Mutex::lock: no current task");
+ }
+ if (curr->preempt_count > 0) {
+ kernel_panic("Mutex::lock: preemption disabled (holding spinlock?)");
+ }
+
+ wait_lock_.acquire();
+
+ if (owner_ == nullptr) {
+ owner_ = curr;
+ wait_lock_.release();
+ return;
+ }
+
+ if (owner_ == curr) {
+ wait_lock_.release();
+ kernel_panic("Mutex::lock: recursive acquire by same task");
+ }
+
+ /* Slow path: enqueue ourselves and sleep until we are head + lock free. */
+ MutexWaiter waiter;
+ waiter.link.init();
+ waiter.task = curr;
+ list_add_tail(&waiter.link, &wait_list_);
+
+ for (;;) {
+ /* Set state inside wait_lock_ so unlock-side sees a coherent view:
+ * either we are on the list AND Sleeping (will be woken), or we
+ * already took ownership above (not in this loop). */
+ curr->state = TaskState::Sleeping;
+ wait_lock_.release();
+
+ schedule();
+
+ wait_lock_.acquire();
+
+ if (owner_ == nullptr && wait_list_.next == &waiter.link) {
+ owner_ = curr;
+ list_del_init(&waiter.link);
+ wait_lock_.release();
+ return;
+ }
+ /* Spurious wake / lost race — keep waiting. */
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* unlock */
+/* ------------------------------------------------------------------ */
+
+void Mutex::unlock()
+{
+ TaskControlBlock* curr = current_task();
+
+ wait_lock_.acquire();
+
+ if (owner_ != curr) {
+ wait_lock_.release();
+ kernel_panic("Mutex::unlock: not the owner");
+ }
+
+ owner_ = nullptr;
+
+ if (!wait_list_.is_empty()) {
+ list_node* first = wait_list_.next;
+ MutexWaiter* w = list_entry<MutexWaiter, &MutexWaiter::link>(first);
+ TaskControlBlock* victim = w->task;
+
+ /* Race-tolerant wake: only enqueue if still sleeping. If the
+ * waiter is already Runnable (e.g. it was already woken by a
+ * timer-driven schedule reentry and didn't yet retry the lock),
+ * sched_enqueue would double-link it. */
+ if (victim->state == TaskState::Sleeping) {
+ victim->state = TaskState::Runnable;
+ sched_enqueue(victim);
+ }
+ }
+
+ wait_lock_.release();
+}
+
+/* ------------------------------------------------------------------ */
+/* Debug accessors */
+/* ------------------------------------------------------------------ */
+
+bool Mutex::is_owned_by_current() const
+{
+ return owner_ == current_task();
+}