diff --git a/src/sched.c b/src/sched.c index 52e0686..51170d1 100644 --- a/src/sched.c +++ b/src/sched.c @@ -189,26 +189,145 @@ bumped ahead of that again (176 -> 184) instead of waiting to observe it, same reasoning as every bump above. cwdtest (boot-time spawned, real per-process working directory live demo -- sched.h's new Thread.cwd) + plus cwdtarget (1 more, runtime-spawned by cwdtest itself, the same + *test / *target relationship jobtest/jobtarget already have) are two + more concurrently live threads, so bumped again (184 -> 192) for the + same headroom reason as every bump above. envprint (loaded on demand by + the real shell, like jobctlfg/hcat/pipecat above, never boot-spawned) + plus a kernel-only driver thread (kmain.c's envDriverThread, which types + `export`/`envprint`/`env` at the real shell through the same + injected-keystroke mechanism every driver thread above already uses) + would immediately re-create the exact same class of bug, so bumped + ahead of that again (192 -> 200) instead of waiting to observe it, same + reasoning as every bump above. nicetest (boot-time spawned, real + userspace-triggered sysNice/sysGetPriority live demo) plus nicetarget + (1 more, runtime-spawned by nicetest itself while sleeping so it can be + reniced while blocked, the same *test / *target relationship + jobtest/jobtarget and cwdtest/cwdtarget already have) are two more + concurrently live threads, so bumped again (200 -> 208) for the same + headroom reason as every bump above. date (boot-time spawned, real + sysTime/rtc.c live demo, fully synchronous like nicetest with no + runtime-spawned partner of its own) is one more, so bumped again + (208 -> 216) for the same headroom reason as every bump above. timetest + (boot-time spawned, real SFS inode mtime/ctime live demo through userOpen/ + userWrite/userStat/userLink, fully synchronous like nicetest/date with no + runtime-spawned partner) is one more, so bumped again (216 -> 224) for the + same headroom reason as every bump above. lsDriverThread (kmain.c, boot- + time kernel thread that types `mkdir`/`echo`/`ls -l` at the real shell + for shell.c's new generalized `ls` builtin) is one more, so bumped again + (224 -> 232) for the same headroom reason as every bump above. + andOrDriverThread (kmain.c, boot-time kernel thread that types + `false && echo X || echo ao2` at the real shell for shell.c's new + `&&`/`||` conditional chaining) is one more, so bumped again + (232 -> 240) for the same headroom reason as every bump above. rmtest + (boot-time spawned, real vfsRmdir/sysRmdir unlink(2)-vs-rmdir(2) split + live demo on both tmpfs and SFS, fully synchronous like nicetest/date/ + timetest with no runtime-spawned partner) is one more, so bumped again + (240 -> 248) for the same headroom reason as every bump above. permtest + (boot-time spawned, real chmod(2)/owner-rwx-permission-bits live demo) + plus the one short-lived runtime-spawned copy of /bin/false it chmods and + spawns to prove the exec-permission check, are two more concurrently + live threads, so bumped again (248 -> 256) for the same headroom reason + as every bump above. dirpermtest (boot-time spawned, real directory-mode + enforcement live demo -- vfsMkdir/resolveOpen/vfsUnlink/vfsRmdir's new + parent-write and ancestor-search checks, lookupNode/lookupFrom's new + exec-on-traversal check -- fully synchronous like identtest with no + runtime-spawned partner) is one more, so bumped again (256 -> 264) for + the same headroom reason as every bump above. grouptest (boot-time + spawned, real group-credential/vfsPermitted-group-branch live demo plus + the two short-lived runtime-spawned copies of /bin/false it group-execs + to prove the exec-permission check across matching/mismatched groups) + are two more concurrently live threads, so bumped again (264 -> 272) for + the same headroom reason as every bump above. pidfdtest (boot-time + spawned, real pidfd.h live demo through sysPidfdOpen/sysPidfdSend/ + sysPidfdWait) plus the one runtime-spawned copy of /bin/killvictim it + reuses as its target (same proven "genuinely stuck" shape killtest.c + already established) are two more concurrently live threads, so bumped + again (272 -> 280) for the same headroom reason as every bump above. + mmaptest (boot-time spawned, real mmap(2)/munmap(2) demand-paging live + demo through sysMmap/sysMunmap -- anonymous demand-zero, file-backed + read-through, and a real SIGSEGV proven against a PROT_READ-only + mapping) plus the one runtime-spawned copy of /bin/mmapprotvictim it + uses to prove that last case are two more concurrently live threads, so + bumped again (280 -> 288) for the same headroom reason as every bump + above. */ +#define maxThreads 288 #define threadStackSize 16384 +/* Virtual-runtime charge for one timer tick's worth of execution at + schedDefaultWeight; scaled by schedDefaultWeight/weight for other + weights (see threadSetPriority). Purely an internal unit -- only ever + compared against other vruntime values, never against wall-clock time. */ +#define schedTickCharge 1000000ull + static Thread threads[maxThreads]; static uint32 threadTotal; static uint32 currentIndex; static bool schedulerActive; +/* allocSlot's own round-robin cursor -- see that function's doc comment + for why this exists at all, not just "the lowest free index" the way + threadTotal's own high-water-mark growth already reads left to right. */ +static uint32 nextSlotHint = 1; + +/* Thread.generation's own monotonic source (see that field's doc comment, + sched.h) -- never 0 (0 is reserved to mean "this slot is currently + unoccupied", the same convention pipe.c's nextPipeIncarnation already + uses for the identical reason), never reused, incremented once per + threadCreate/threadCreateUser regardless of which slot the new thread + lands in. */ +static uint32 nextThreadGeneration = 1; + +/* Bounded copy into a Thread's own name buffer (see threadNameMax / + sched.h) -- never aliases the caller's storage, since threadCreateUser + is called from syscall.c with a path that lives in a stack-local buffer + for the duration of one syscall. */ +static void copyThreadName(Thread* thread, const char* name) +{ + uint64 len = strlen(name); + if (len >= threadNameMax) + len = threadNameMax - 1; + memcpy(thread->name, name, len); + thread->name[len] = 0; +} + +void threadSetName(uint32 id, const char* name) +{ + if (id >= threadTotal) + return; + copyThreadName(&threads[id], name); +} + void schedInit(void) { threadTotal = 1; currentIndex = 0; threads[0].id = 0; - threads[0].name = "idle"; + threads[0].parentId = 0; + /* Reserved, permanently: idle is never reaped/recreated, and pid 0 is + already rejected as a pidfd/kill/wait target everywhere else, so it + never needs a real incarnation of its own (see Thread.generation's + own doc comment). */ + threads[0].generation = 0; + threads[0].pgid = 0; + threads[0].uid = 0; + threads[0].gid = 0; + threads[0].cwd[0] = '/'; + threads[0].cwd[1] = 0; + threads[0].envCount = 0; + copyThreadName(&threads[0], "idle"); threads[0].state = threadRunning; threads[0].rsp = 0; threads[0].stackBase = 0; threads[0].kernelStackTop = 0; threads[0].sleepUntil = 0; threads[0].brk = 0; + threads[0].vruntime = 0; + threads[0].exitCode = 0; + threads[0].weight = schedDefaultWeight; + threads[0].everRan = true; + threads[0].waitChannel = nullptr; vfsFdInit(threads[0].fds); }