From 785e147d44df2224cce092fe83c6ea16874756e1 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 1 Sep 2026 22:36:26 +0200 Subject: [PATCH] feat: build out sched --- include/sched.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/include/sched.h b/include/sched.h index 1641638..64d63e6 100644 --- a/include/sched.h +++ b/include/sched.h @@ -452,6 +452,83 @@ WaitScanResult threadJoinAnyChild(uint32 parentId, uint32* outId, int64* outCode so a parent genuinely blocked in userWaitAny wakes the instant ANY of its children exits, not just one it named specifically. */ void threadExit(uint64 code); + +/* Exit code threadKill assigns its victim -- named after SIGKILL (9), the + nearest real-world equivalent, though this kernel has no signal delivery + at all: threadKill is a hard, unconditional termination, not something a + target thread can catch, mask, or clean up after (see threadKill). */ +#define threadKilledExitCode 9u + +/* Unconditionally terminates thread `id` from OUTSIDE it -- the only way to + ever get back a process that is genuinely stuck (e.g. blocked forever in + a syscall-level pipe read with no writer ever coming, now that + sysRead/sysWrite can really block, see pipe.h/vfs.h). Works on a thread + in any of Runnable/Sleeping/Blocked: its state is simply set straight to + threadDead with exitCode threadKilledExitCode, exactly as if it had + called threadExit(threadKilledExitCode) itself, and whoever is (or later + becomes) threadJoin()ing it wakes up and reaps it normally -- there is no + "half-dead" state and no cleanup callback, since a mid-syscall thread has + no user-mode cleanup path to run anyway. Returns false, changing nothing, + for id 0 (idle), an out-of-range id, an already-Dead/never-created slot, + or (implicitly, since only the CPU's one currently-executing thread is + ever threadRunning) the caller's own id -- a syscall handler always runs + as its OWN thread, which is Running, not Runnable/Sleeping/Blocked, at + the moment it would try to kill itself, so self-kill is naturally + rejected without a special-case check. Real termination (sysExit) is + still how a thread should ever end itself. Also raises sigChld against + the victim's parentId, exactly like threadExit does -- a killed child + is still a dead child as far as its parent's SIGCHLD is concerned, the + same way real kill(2)ing a child still generates SIGCHLD to its real + parent. Also wakes the victim's parent's OWN slot as a wait channel, + exactly like threadExit does -- a parent parked in userWaitAny does not + care whether a child exited on its own or was killed, same as real + waitpid(-1) does not distinguish the two either. */ +bool threadKill(uint32 id); + +/* Job control (signal.h): unconditionally moves thread `id` -- Runnable, + Running (including the caller itself, unlike threadKill which naturally + excludes self-targeting), Sleeping, or Blocked -- into threadStopped. + Idempotent if `id` is already Stopped (matches real kill(pid, SIGSTOP) + succeeding again on an already-stopped process). Safe on a + Sleeping/Blocked thread for the same reason threadKill already is: every + such thread is parked mid-retry-loop right after its own halt() (see + pipeReadBlocking/pipeWriteBlocking, or the restart-syscall pattern + syscall.c's sysRead/sysWrite/sysWait use), so simply not being picked by + schedule() again until threadContinue moves it back is enough -- no + wait-channel state is lost, since that retry loop re-checks its own + condition from scratch every time it runs at all. Returns false, + changing nothing, for id 0 (idle -- stopping it would wedge the whole + machine the instant nothing else was runnable), an out-of-range id, or + an already-Dead/never-created slot. */ +bool threadStop(uint32 id); + +/* Job control (signal.h): moves thread `id` from threadStopped back to + threadRunnable. Returns false, changing nothing, if `id` is out of + range or not currently Stopped (matches real SIGCONT delivered to a + process that was never stopped -- a harmless no-op at the process-state + level; signal.c's sigRaise still delivers the signal itself/runs a + handler regardless of this return value). */ +bool threadContinue(uint32 id); + +/* Real Unix's interruptible sleep/EINTR: forces thread `id` from + threadSleeping or threadBlocked straight to threadRunnable, early -- + before its sleep timer expires or its wait-channel condition is ever + satisfied -- so the NEXT schedule() eventually resumes it, letting + isr.c's deliverPendingSignals (which only ever runs at a return-to-ring3 + point) actually reach a thread that would otherwise never naturally + produce one. Called only by signal.c's sigRaise, and only for a signal it + has already determined would genuinely be delivered (not blocked, not + ignored) if this thread resumed right now -- callers must not use this to + wake a thread for an unrelated reason. Deliberately excludes + threadStopped (only threadContinue, an explicit SIGCONT, may ever move a + Stopped thread -- the same asymmetry schedWakeChannel already has, for + the same reason: job control's "stopped means stopped" must survive an + ordinary signal arriving too). Returns false, changing nothing, for an + out-of-range id or any state other than Sleeping/Blocked (Runnable/ + Running need no forcing; Stopped/Dead/Unused have nothing to wake, or + must not be woken this way at all). */ +bool threadWakeForSignal(uint32 id); + void threadSleep(uint64 milliseconds); void threadSetBrk(uint32 id, uint64 brk); uint64 currentThreadSbrk(int64 increment);