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:
@@ -232,6 +232,7 @@ fn system_call(state: *architecture.CpuState) void {
|
||||
.current_core => systemCurrentCore(state),
|
||||
.thread_self => systemThreadSelf(state),
|
||||
.thread_join => systemThreadJoin(state),
|
||||
.set_thread_pointer => systemSetThreadPointer(state),
|
||||
.futex_wait => systemFutexWait(state),
|
||||
.futex_wake => systemFutexWake(state),
|
||||
.thread_exit => {
|
||||
@@ -707,6 +708,20 @@ fn systemThreadSelf(state: *architecture.CpuState) void {
|
||||
architecture.setSystemCallResult(state, scheduler.currentId());
|
||||
}
|
||||
|
||||
/// set_thread_pointer(addr) -> 0: set the caller's FS base (its user-space TLS thread
|
||||
/// pointer). The kernel never uses FS; the scheduler restores this per task across context
|
||||
/// switches (docs/threading-plan.md M10). `addr` must be a user-half address.
|
||||
fn systemSetThreadPointer(state: *architecture.CpuState) void {
|
||||
const addr = architecture.systemCallArg(state, 0);
|
||||
const t = scheduler.current();
|
||||
if (t.aspace == 0) return fail(state); // kernel tasks have no user TLS
|
||||
if (addr >= user_half_end) return fail(state);
|
||||
const flags = sync.enter();
|
||||
scheduler.setThreadPointerLocked(addr);
|
||||
sync.leave(flags);
|
||||
architecture.setSystemCallResult(state, 0);
|
||||
}
|
||||
|
||||
/// thread_join(tid) -> 0: block until the thread with id `tid` has exited (docs/threading-
|
||||
/// plan.md M9). Needs no per-thread IPC endpoint. The compare-and-block is one critical
|
||||
/// section, so an exit cannot slip between "is it alive?" and the block.
|
||||
|
||||
Reference in New Issue
Block a user