threads(M10): per-thread fs.base — the TLS thread-pointer mechanism
Each thread gets its own x86_64 thread pointer (FS base) for user-space TLS. Task.fs_base is restored on every context switch only when it changes (same conditional-load discipline as CR3; architecture.setFsBase -> wrmsr IA32_FS_BASE). New set_thread_pointer=44 syscall sets the caller's fs_base and loads it now. The kernel never touches FS, so no swapgs complication. The runtime lays a small per-thread TLS block at the top of each thread's stack (self-pointer at %fs:0 + scratch) and the thread trampoline calls set_thread_pointer before any user code — so every spawned thread has a private, switch-stable thread pointer, reclaimed with the stack. thread-test tls mode: two threads write unique markers to their own %fs:8 and, after both wrote, read back — a shared fs.base would clobber one (cross-talk). Deferred: the Zig threadlocal *compiler* layer (ELF variant-II PT_TLS + linker sections + template copy) — high-uncertainty, no consumer today; this lands the load-bearing per-thread fs.base it builds on. See docs/threading-plan.md M10. Gate thread-tls PASS (3x); full guardrail 25/25; build + host tests clean.
This commit is contained in:
@@ -155,6 +155,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
threadAllocTest(boot_information);
|
||||
} else if (eql(case, "task-reap")) {
|
||||
taskReapTest(boot_information);
|
||||
} else if (eql(case, "thread-tls")) {
|
||||
threadTlsTest(boot_information);
|
||||
} else if (eql(case, "args")) {
|
||||
argsTest(boot_information);
|
||||
} else if (eql(case, "init")) {
|
||||
@@ -1740,6 +1742,49 @@ fn threadAllocTest(boot_information: *const BootInformation) void {
|
||||
result();
|
||||
}
|
||||
|
||||
/// Per-thread TLS / fs.base (docs/threading-plan.md M10): `thread-test` in tls mode has two
|
||||
/// threads each set their own FS base and write a unique marker to `%fs:8`, then — after
|
||||
/// both have written — read it back. If fs.base were not per-thread and restored across
|
||||
/// context switches, the second write would clobber the first and a thread would read the
|
||||
/// wrong marker. The verdict marker means both read their own value (no cross-talk).
|
||||
fn threadTlsTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: thread-tls\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over an initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
const rd = initial_ramdisk.Reader.init(image) orelse {
|
||||
check("initial_ramdisk image is valid", false);
|
||||
result();
|
||||
return;
|
||||
};
|
||||
|
||||
var started = false;
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
const item = rd.entry(i) orelse continue;
|
||||
if (!eql(item.name, "thread-test")) continue;
|
||||
started = if (process.spawnProcess(item.blob, 4, &.{ "thread-test", "tls" })) true else |_| false;
|
||||
break;
|
||||
}
|
||||
check("thread-test (tls mode) spawned", started);
|
||||
|
||||
const ok_marker = "thread-tls: ok";
|
||||
const fail_marker = "thread-tls: FAIL";
|
||||
scheduler.setPriority(1);
|
||||
const deadline = architecture.millis() + 12000;
|
||||
while (architecture.millis() < deadline) {
|
||||
if (bufferHas(ok_marker) or bufferHas(fail_marker)) break;
|
||||
scheduler.yield();
|
||||
}
|
||||
scheduler.setPriority(4);
|
||||
|
||||
check("each thread has its own fs.base TLS slot (no cross-talk across switches)", bufferHas(ok_marker) and !bufferHas(fail_marker));
|
||||
result();
|
||||
}
|
||||
|
||||
/// The task reaper (docs/threading-plan.md M8): a dead task's kernel stack used to be
|
||||
/// leaked ("no reaper yet"). Spawn and kill many ring-3 processes and confirm the total
|
||||
/// kernel-stack bytes return to baseline — every stack reclaimed, no leak. (Threads exit
|
||||
|
||||
Reference in New Issue
Block a user