summaryrefslogtreecommitdiff
path: root/src/kernel/spinlock.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2025-12-30 14:21:40 +0800
committerRichard Qin <richard.qin.x@gmail.com>2025-12-30 14:21:40 +0800
commit7759eef8cdac79028dd5b468bd9ce0cf1c12b162 (patch)
treecea3f9e1857641d824129828a7a0ca6cb7e84431 /src/kernel/spinlock.cc
parent588884418a2dd0110209ef1673c4ab0c47173254 (diff)
downloadlume-7759eef8cdac79028dd5b468bd9ce0cf1c12b162.tar.gz
lume-7759eef8cdac79028dd5b468bd9ce0cf1c12b162.tar.xz
dev(spinlock): refactoring the implementation of locks
Diffstat (limited to 'src/kernel/spinlock.cc')
-rw-r--r--src/kernel/spinlock.cc221
1 files changed, 151 insertions, 70 deletions
diff --git a/src/kernel/spinlock.cc b/src/kernel/spinlock.cc
index 210e0b9..426f639 100644
--- a/src/kernel/spinlock.cc
+++ b/src/kernel/spinlock.cc
@@ -2,106 +2,187 @@
#include "kernel/riscv.h"
#include "kernel/proc.h"
#include "drivers/uart.h"
-#include "common/types.h"
-#define NCPU 8
+extern struct cpu cpus[];
-namespace Spinlock
+// Get the pointer of the current CPU
+// Depends on r_tp() in riscv.h to read the tp register
+struct cpu *mycpu()
{
- int g_noff[NCPU];
+ int id = r_tp();
+ return &cpus[id];
+}
- int g_intena[NCPU];
+// Get the current process
+// Must be called with interrupts disabled, or read atomically in a safe manner
+struct Proc *myproc()
+{
+ push_off();
+ struct cpu *c = mycpu();
+ struct Proc *p = c->proc;
+ pop_off();
+ return p;
+}
- int cpuid()
- {
- return r_tp();
- }
+// Interrupt Depth Management
+void push_off(void);
+void pop_off(void);
+
+// Spinlock Implementation
+
+void Spinlock::init(const char *lock_name)
+{
+ name = lock_name;
+ locked = 0;
+ cpu = nullptr;
+}
- void init(struct spinlock *lk, char* name)
+void Spinlock::acquire()
+{
+ push_off(); // Disable interrupts: prevent deadlocks (interrupt handler requests a lock that is already held)
+
+ if (holding())
{
- lk->name = name;
- lk->locked = 0;
- lk->cpu_id = -1;
+ Drivers::uart_puts("panic: recursive acquire ");
+ Drivers::uart_puts(name);
+ Drivers::uart_puts("\n");
+ while (1)
+ ;
}
- void push_off()
+ // Atomic Exchange
+ // __ATOMIC_ACQUIRE: Ensures that reads and writes after the acquire are not reordered before the acquire
+ while (__atomic_exchange_n(&locked, 1, __ATOMIC_ACQUIRE))
{
- int old = intr_get();
- intr_off();
-
- int id = cpuid();
- if (g_noff[id] == 0)
+ // Busy-wait (Spinning)
+ // Optimization: Read-only, no writes, reducing cache coherence traffic
+ while (__atomic_load_n(&locked, __ATOMIC_RELAXED) == 1)
{
- g_intena[id] = old;
+ asm volatile("nop"); // RISC-V indicates that the CPU is in a spin state
}
- g_noff[id] += 1;
}
- void pop_off()
+ // Record holder
+ cpu = mycpu();
+}
+
+void Spinlock::release()
+{
+ if (!holding())
{
- int id = cpuid();
- if (intr_get())
- Drivers::uart_puts("pop_off - interruptible\n");
+ Drivers::uart_puts("panic: release not holding\n");
+ while (1)
+ ;
+ }
- if (g_noff[id] < 1)
- Drivers::uart_puts("pop_off - count error\n");
+ cpu = nullptr;
- g_noff[id] -= 1;
+ // Atomic Store
+ // __ATOMIC_RELEASE: Ensures all reads and writes before the Release are completed
+ __atomic_store_n(&locked, 0, __ATOMIC_RELEASE);
- if (g_noff[id] == 0 && g_intena[id])
- {
- intr_on();
- }
- }
+ pop_off(); // 2. 恢复中断状态
+}
+
+// Check if the current CPU holds the lock
+bool Spinlock::holding()
+{
+ // Interrupt checks must be disabled, otherwise a process switch may occur causing CPU fluctuations.
+ int r;
+ push_off();
+ r = (locked && cpu == mycpu());
+ pop_off();
+ return r;
+}
+
+
+// Mutex Implementation (Sleep Locks)
+
+void Mutex::init(const char *lock_name)
+{
+ name = lock_name;
+ locked = 0;
+ pid = 0;
+ lk.init("mutex_internal"); // 初始化内部自旋锁
+}
+
+void Mutex::acquire()
+{
+ lk.acquire(); // Acquire the internal lock to protect the locked field
- int holding(struct spinlock *lk)
+ while (locked)
{
- int r;
- push_off();
- r = (lk->locked && lk->cpu_id == cpuid());
- pop_off();
- return r;
+ // Lock is held, preparing to sleep
+ // Atomicity contract of sleep():
+ // 1. Mark the current process as SLEEPING
+ // 2. Release lk (so others can operate on locked)
+ // 3. Switch CPU (context switch)
+ // 4. After being woken up, reacquire the lk lock and return
+ ProcManager::sleep(this, &lk);
}
- void acquire(struct spinlock *lk)
- {
- push_off();
+ locked = 1;
+ pid = myproc()->pid;
- if(holding(lk))
- {
- Drivers::uart_puts("acquire: panic - recursive lock ");
- Drivers::uart_puts(lk->name);
- Drivers::uart_puts("\n");
- while (1)
- ;
- }
+ lk.release();
+}
- while (__sync_lock_test_and_set(&lk->locked, 1) != 0)
- {
- // 自旋等待
- }
+void Mutex::release()
+{
+ lk.acquire();
- __sync_synchronize();
+ locked = 0;
+ pid = 0;
- lk->cpu_id = cpuid();
- }
+ // Wake up all processes sleeping on this lock (chan = this)
+ ProcManager::wakeup(this);
- void release(struct spinlock *lk)
- {
- if (!holding(lk))
- {
- Drivers::uart_puts("release: panic - not holding\n");
- while (1)
- ;
- }
+ lk.release();
+}
- lk->cpu_id = -1;
+bool Mutex::holding()
+{
+ lk.acquire(); // Must use a lock when reading to prevent race conditions.
+ bool r = locked && (pid == myproc()->pid);
+ lk.release();
+ return r;
+}
+
+// Interrupt Depth Management
+
+void push_off(void)
+{
+ int old = intr_get();
+ intr_off();
+
+ struct cpu *c = mycpu();
- __sync_synchronize();
+ if (c->noff == 0)
+ c->intena = old; // 记录最外层的中断状态
- __sync_lock_release(&lk->locked);
+ c->noff += 1;
+}
- pop_off();
+void pop_off(void)
+{
+ struct cpu *c = mycpu();
+
+ if (intr_get())
+ {
+ Drivers::uart_puts("panic: pop_off - interruptible\n");
+ while (1)
+ ;
}
+ if (c->noff < 1)
+ {
+ Drivers::uart_puts("panic: pop_off count\n");
+ while (1)
+ ;
+ }
+
+ c->noff -= 1;
-} // namespace Spinlock
+ // An interrupt is only truly enabled when the nesting level returns to zero and the outermost layer allows interrupts.
+ if (c->noff == 0 && c->intena)
+ intr_on();
+} \ No newline at end of file