threads(M4): futex_wait/futex_wake, the blocking primitive
New private syscalls futex_wait(addr, expected, timeout_ns)=40 and futex_wake(addr, count)=41. A waiter is a .blocked task tagged with Task.futex_addr (no queue linkage); futex_wait reads the user word under the big lock and parks only if it still equals expected, so a concurrent wake can't slip between the check and the block. futex_wake scans the task table and readies up to count waiters in the same address space. A timed wait also sets wake_at so the existing wakeExpired times it out; futex_addr staying non-zero (only futex_wake clears it) distinguishes timeout from a real wake. Waiters park in-kernel, so an idle core still halts (no busy-wait). runtime.Thread.Futex mirrors std.Thread.Futex (wait/timedWait/wake). thread-test gains a futex mode: a waiter parks, the main thread wakes it (serial order waiting/waking/woke, asserted by the case regex), and timedWait reports a timeout. Gate thread-futex PASS (3x); 18 guardrail cases green incl. sleep/event/ipc blocking paths; build + host tests clean.
This commit is contained in:
@@ -80,6 +80,9 @@ pub const Task = struct {
|
||||
user_sp: u64 = 0, // user-mode stack pointer (user task only)
|
||||
user_arg: u64 = 0, // value delivered in the user's rdi at first entry: 0 for a
|
||||
// process (its _start ignores it), the closure pointer for a thread (docs/threading.md)
|
||||
// The user address this task is blocked on in futex_wait (0 = not futex-waiting).
|
||||
// Cleared to 0 by futexWakeLocked as the "woken, not timed out" signal (docs/threading.md).
|
||||
futex_addr: u64 = 0,
|
||||
// Next free virtual address in this task's mmap grant arena (0 = uninitialised;
|
||||
// process.zig lazily seeds it to the arena base on the first mmap). Bumped up
|
||||
// as the user heap grows; user task only.
|
||||
@@ -516,6 +519,49 @@ pub fn sleep(ms: u64) void {
|
||||
sync.leave(flags);
|
||||
}
|
||||
|
||||
// --- futex: block/wake on a user address (docs/threading.md) ----------------
|
||||
//
|
||||
// A futex waiter is not linked into any queue — it is simply a `.blocked` task
|
||||
// tagged with the address it waits on (`futex_addr`). Waking scans the task table
|
||||
// (bounded) for matching waiters. A timed wait also sets `wake_at`, so the timer's
|
||||
// `wakeExpired` can wake it; `futex_addr` stays non-zero in that case, which is how
|
||||
// the waiter tells a timeout from a real wake.
|
||||
|
||||
pub const FutexResult = enum { woken, timed_out };
|
||||
|
||||
/// Block the current task on futex `addr` until woken, or (if `timeout_ms > 0`) the
|
||||
/// deadline. **Precondition:** the big kernel lock is held and the caller has already
|
||||
/// checked, under this same lock, that the futex word equals the expected value — so
|
||||
/// no wake can be missed. Returns with the lock still held.
|
||||
pub fn futexWaitLocked(addr: u64, timeout_ms: u64) FutexResult {
|
||||
const t = current();
|
||||
t.futex_addr = addr;
|
||||
t.wake_at = if (timeout_ms > 0) architecture.millis() + timeout_ms else 0;
|
||||
t.state = .blocked;
|
||||
schedule(); // woken by futexWakeLocked (clears futex_addr) or wakeExpired (timeout)
|
||||
const woken = t.futex_addr == 0;
|
||||
t.futex_addr = 0;
|
||||
t.wake_at = 0;
|
||||
return if (woken) .woken else .timed_out;
|
||||
}
|
||||
|
||||
/// Wake up to `count` tasks blocked in `futex_wait` on `addr` in address space
|
||||
/// `aspace`. Precondition: the big kernel lock is held. Returns how many woke.
|
||||
pub fn futexWakeLocked(aspace: u64, addr: u64, count: u32) u32 {
|
||||
var woken: u32 = 0;
|
||||
for (&tasks) |*t| {
|
||||
if (woken >= count) break;
|
||||
if (t.state == .blocked and t.aspace == aspace and t.futex_addr == addr) {
|
||||
t.futex_addr = 0; // the "woken, not timed out" signal to futexWaitLocked
|
||||
t.wake_at = 0;
|
||||
t.state = .ready;
|
||||
enqueue(t);
|
||||
woken += 1;
|
||||
}
|
||||
}
|
||||
return woken;
|
||||
}
|
||||
|
||||
// --- event-based blocking -------------------------------------------------
|
||||
//
|
||||
// A WaitQueue is a set of tasks blocked waiting for something (a resource, a
|
||||
|
||||
Reference in New Issue
Block a user