diff --git a/src/kern/mutex.zig b/src/kern/mutex.zig index a71b9b5..a445e09 100644 --- a/src/kern/mutex.zig +++ b/src/kern/mutex.zig @@ -2,6 +2,9 @@ const std = @import("std"); const ke = @import("root").ke; const ki = ke.private; +/// Number of spins before blocking. +const optimistic_spins = 10000; + pub const Mutex = struct { /// The thread currently holding the mutex, null if unlocked. owner: std.atomic.Value(?*ke.Thread), @@ -14,10 +17,14 @@ pub const Mutex = struct { const ipl = ke.ipl.raise(.Dispatch); const curtd = ki.sched.percpu.local().current_thread.?; - // Fast path: try to acquire without a turnstile. - if (m.owner.cmpxchgStrong(null, curtd, .acquire, .monotonic) == null) { - ke.ipl.lower(ipl); - return; + // Fast path: try to acquire without a turnstile by spinning a bit. + for (0..optimistic_spins) |_| { + if (m.owner.cmpxchgStrong(null, curtd, .acquire, .monotonic) == null) { + ke.ipl.lower(ipl); + return; + } + + std.atomic.spinLoopHint(); } // Slow path: contended, block on a turnstile. @@ -35,7 +42,7 @@ pub const Mutex = struct { } // Block until woken. - ki.turnstile.block(ts, m, owner.?, .Shared); + ki.turnstile.block(ts, m, owner.?, .Exclusive); // Re-try acquisition after wakeup. if (m.owner.cmpxchgStrong(null, curtd, .acquire, .monotonic) == null) { @@ -62,7 +69,7 @@ pub const Mutex = struct { // Note: wake up all waiters. This so-called "lock barging" (name from WTF::ParkingLot) // has been shown to be better because this avoids lock convoys, see this mysterious 70s paper: // https://dl.acm.org/doi/pdf/10.1145/850657.850659 - ki.turnstile.wakeup(ts.?, .Exlusive, ts.?.waiters, null); + ki.turnstile.wakeup(ts.?, .Exclusive, ts.?.waiters, null); ke.ipl.lower(ipl); }