reorg: split the runtime into library/kernel concern modules (C1)

The one giant `runtime` module (with a `system.zig` that was itself a dumping
ground of unrelated syscalls) is split into directly-importable, flat concern
modules under library/kernel/:

  system-call  ipc  memory  process  thread  time  logging  file-system  service  start
  (+ the device/service clients: device, device-manager, block, display, input)

system.zig is dissolved — its functions moved to their concern home (mmap ->
memory, spawn/kill/exit -> process, sleep/clock -> time, write/klog -> logging,
fs* -> file-system). `memory` merges heap+dma+shared-memory behind one flat API
(memory.allocator/dmaAlloc/sharedCreate/mmap), keeping heap's state and malloc
export single. The memory<->thread dependency cycle (heap needs Thread.Mutex,
thread needs mmap) is broken by having thread allocate its own stack via the raw
mmap syscall, so the module graph is a DAG.

This is the atomic step: all 42 internal cross-imports flip from relative to
module imports at once. `runtime.zig` and `system.zig` become thin re-export
SHIMS so the ~38 consumers keep compiling on `runtime.*` untouched; they migrate
to direct imports in C2, after which the shims are deleted (C5).

zig build + zig build test green; 14 QEMU cases pass (smoke, process,
process-kill, thread-spawn/join, logger, vfs, fat-mount, display-native,
usb-storage, virtio-gpu, device-manager, input, power-button).
This commit is contained in:
Daniel Samson
2026-07-22 22:53:13 +01:00
parent 3e69712b97
commit 60f32ee9ff
21 changed files with 636 additions and 467 deletions
+17 -6
View File
@@ -13,8 +13,19 @@
const std = @import("std");
const builtin = @import("builtin");
const abi = @import("abi");
const sc = @import("system-call.zig");
const system = @import("system.zig");
const sc = @import("system-call");
// A thread allocates its own stack straight from the mmap syscall (not through the
// `memory` module) so `memory`'s heap can depend on this module's Mutex without a cycle.
inline fn mmapStack(len: usize) usize {
return sc.systemCall2(.mmap, len, abi.prot_read | abi.prot_write);
}
inline fn mmapFailed(ret: usize) bool {
return ret > ~@as(usize, 0) - 4095;
}
inline fn munmapStack(base: usize, len: usize) void {
_ = sc.systemCall2(.munmap, base, len);
}
/// True in a real danos binary; false when this module is compiled for host unit tests.
/// The `Futex` seam and the test blocks below branch on it so the lock/condvar state
@@ -66,8 +77,8 @@ pub const Thread = struct {
}
};
const base = system.mmap(config.stack_size, system.PROT_READ | system.PROT_WRITE);
if (system.mmapFailed(base)) return error.SystemResources;
const base = mmapStack(config.stack_size);
if (mmapFailed(base)) return error.SystemResources;
// Top of the thread's own stack, downward: the closure, then a small per-thread TLS
// block (the thread pointer points here; slot 0 is the variant-II self-pointer, the rest is
@@ -88,7 +99,7 @@ pub const Thread = struct {
const tid = threadSpawn(@intFromPtr(&Closure.entry), stack_top, closure_addr);
if (threadSpawnFailed(tid)) {
_ = system.munmap(base, config.stack_size);
munmapStack(base, config.stack_size);
return error.SystemResources;
}
return .{ .tid = @intCast(tid), .stack_base = base, .stack_size = config.stack_size };
@@ -99,7 +110,7 @@ pub const Thread = struct {
/// child-exit notification on it is this thread's.
pub fn join(self: Thread) void {
_ = sc.systemCall1(.thread_join, self.tid); // block until the thread has exited
_ = system.munmap(self.stack_base, self.stack_size); // reclaim its (now-vacated) stack
munmapStack(self.stack_base, self.stack_size); // reclaim its (now-vacated) stack
}
/// Relinquish the right to join: never wait for or reclaim this thread. Its stack is