threads(M7): thread-safe allocation (per-aspace mmap arena + locked heap)
Move the mmap/mmio grant-arena cursors off Task into the per-address-space object (scheduler aspace_refs, exposed via aspaceMmapNextPtr/aspaceDeviceMapNextPtr), so sibling threads in one address space hand out disjoint grants. systemMmap reserves a range under a brief lock then maps per page under a short-held lock (not the whole grant): the big lock runs with interrupts disabled, so pinning it across a multi-MiB memset+map froze other cores. Guard the runtime heap's rawAlloc/rawFree with a Thread.Mutex, gated on !single_threaded so ordinary binaries compile it out. thread-test gains an alloc mode: 4 threads x 500 alloc/fill/verify/free cycles; any overlap between concurrent allocations is caught by the pattern check. Also fix the affinity guardrail: its 3-billion-iteration busy-loop had codegen-dependent wall-time (adding a function to tests.zig swung it ~4s -> ~63s and timed it out). Reworked to wait on the wall clock instead. Gate thread-alloc PASS (3x); full guardrail 23/23 green; build + host tests clean.
This commit is contained in:
@@ -83,13 +83,9 @@ pub const Task = struct {
|
||||
// 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.
|
||||
heap_next: u64 = 0,
|
||||
// Next free virtual address in this task's MMIO-grant arena (PML4[226]; 0 =
|
||||
// uninitialised, process.zig seeds it on the first mmio_map). User task only.
|
||||
device_map_next: u64 = 0,
|
||||
// The mmap / MMIO grant-arena cursors moved from Task to the per-address-space object
|
||||
// (`AspaceRef`, below) so threads sharing one address space hand out disjoint grants
|
||||
// — see aspaceMmapNextPtr / aspaceDeviceMapNextPtr (docs/threading-plan.md M7).
|
||||
// --- synchronous IPC (ipc_sync.zig) ---
|
||||
// Per-process handle table: a small-int handle names a kernel capability object.
|
||||
// Each entry tags its `kind` (an IPC endpoint or a shared-memory object) so the
|
||||
@@ -148,7 +144,11 @@ var tasks = [_]Task{.{}} ** maximum_tasks;
|
||||
/// only when the **last** task on an address space exits. All access is under the big
|
||||
/// kernel lock. There can be no more live address spaces than tasks, so the table is
|
||||
/// sized to the task pool and never overflows in practice.
|
||||
const AspaceRef = struct { root: u64 = 0, count: u32 = 0 };
|
||||
// The per-address-space kernel object: a reference count plus the grant-arena cursors.
|
||||
// One live entry per address space; threads sharing an address space share this entry,
|
||||
// so their mmap/mmio grants bump one cursor and never overlap (docs/threading-plan.md M7).
|
||||
// `mmap_next`/`device_map_next` are 0 until process.zig seeds them to the arena base.
|
||||
const AspaceRef = struct { root: u64 = 0, count: u32 = 0, mmap_next: u64 = 0, device_map_next: u64 = 0 };
|
||||
var aspace_refs = [_]AspaceRef{.{}} ** maximum_tasks;
|
||||
var aspace_destroy_count: u64 = 0;
|
||||
|
||||
@@ -202,6 +202,26 @@ pub fn liveAspaceCount() u32 {
|
||||
pub fn aspaceDestroyCount() u64 {
|
||||
return aspace_destroy_count;
|
||||
}
|
||||
|
||||
/// Pointer to the mmap grant-arena cursor for address space `root`, so the mmap syscall
|
||||
/// can read-and-bump it. Per-address-space (not per-task), so sibling threads get
|
||||
/// disjoint grants. **Caller holds the kernel lock** (the entry is stable while held).
|
||||
/// Null only if `root` was never retained — which can't happen for a live user task.
|
||||
pub fn aspaceMmapNextPtr(root: u64) ?*u64 {
|
||||
for (&aspace_refs) |*entry| {
|
||||
if (entry.count != 0 and entry.root == root) return &entry.mmap_next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Pointer to the MMIO grant-arena cursor for address space `root` (see
|
||||
/// `aspaceMmapNextPtr`). Caller holds the kernel lock.
|
||||
pub fn aspaceDeviceMapNextPtr(root: u64) ?*u64 {
|
||||
for (&aspace_refs) |*entry| {
|
||||
if (entry.count != 0 and entry.root == root) return &entry.device_map_next;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var next_id: u32 = 1;
|
||||
|
||||
/// Per-CPU scheduler state: the task each core is running, its own idle task, and a
|
||||
|
||||
Reference in New Issue
Block a user