kernel+tests: M4 shared-fate — nine group-death test cases, per-space arena cursors

Eight new QEMU cases (thread-fault-group, kill-threaded-group,
kill-via-worker-tid, racing-triggers, exit-group, leader-thread-exit,
thread-exit-solo, shm-mapping-ref) driving seven new thread-test modes; a
shared checkGroupDead asserts the contract everywhere: one notification,
badged with the leader, reason on the leader's record, no member listed,
claims released first.

The shm-mapping-ref case flushed out the per-task DMA/shared-memory arena
cursor bug directly (a sibling's regions mapped over the worker's), so both
cursors moved to the AddressSpaceRef like the mmap/MMIO cursors before them
(threading M7 pattern). Runtime gains Thread.tryExitCurrent for the leader
-EPERM refusal path. Docs updated: threading.md's shared-fate gap is closed,
process-management.md and process-lifecycle.md describe the leader re-key,
plan status = implemented. Full suite: 100/100.
This commit is contained in:
Daniel Samson
2026-07-22 10:49:46 +01:00
parent 1882161cb4
commit 2bc2a0d70d
10 changed files with 565 additions and 43 deletions
+24 -2
View File
@@ -121,8 +121,10 @@ pub const Task = struct {
ipc_reply_ptr: u64 = 0, // client: reply buffer (virtual_address)
ipc_reply_cap: u64 = 0,
ipc_status: i64 = 0, // client: reply length / -errno, written by the replier
dma_map_next: u64 = 0, // bump pointer into this task's DMA arena (0 = unseeded)
shared_memory_map_next: u64 = 0, // bump pointer into this task's shared-memory arena (0 = unseeded)
// The DMA and shared-memory arena cursors moved to the per-address-space object
// (`AddressSpaceRef`) like the mmap/MMIO cursors before them — per-TASK cursors made
// sibling threads hand out overlapping windows of the one shared arena
// (docs/shared-fate-plan.md M4 tripped exactly that).
ipc_send_cap: u64 = ~@as(u64, 0), // handle to transfer with this message (abi.no_cap = none)
ipc_received_cap: u64 = ~@as(u64, 0), // client: handle the reply's transferred cap landed at (abi.no_cap = none)
next: ?*Task = null, // ready-queue link (also the endpoint sender-FIFO link)
@@ -173,6 +175,8 @@ const AddressSpaceRef = struct {
count: u32 = 0,
mmap_next: u64 = 0,
device_map_next: u64 = 0,
dma_next: u64 = 0, // bump pointer into this space's DMA arena (0 = unseeded)
shared_memory_next: u64 = 0, // bump pointer into this space's shared-memory arena (0 = unseeded)
// Group-death state (docs/shared-fate-plan.md), set once by the first kill
// trigger and never cleared while the entry lives. `dying` gates
// retainAddressSpace — no new member may join a dying group (closing the
@@ -395,6 +399,24 @@ pub fn addressSpaceDeviceMapNextPtr(root: u64) ?*u64 {
}
return null;
}
/// Pointer to the DMA arena cursor for address space `root` (see
/// `addressSpaceMmapNextPtr`). Caller holds the kernel lock.
pub fn addressSpaceDmaNextPtr(root: u64) ?*u64 {
for (&address_space_refs) |*entry| {
if (entry.count != 0 and entry.root == root) return &entry.dma_next;
}
return null;
}
/// Pointer to the shared-memory arena cursor for address space `root` (see
/// `addressSpaceMmapNextPtr`). Caller holds the kernel lock.
pub fn addressSpaceSharedMemoryNextPtr(root: u64) ?*u64 {
for (&address_space_refs) |*entry| {
if (entry.count != 0 and entry.root == root) return &entry.shared_memory_next;
}
return null;
}
var next_id: u32 = 1;
/// Per-CPU scheduler state: the task each core is running, its own idle task, and a