feat: build out sched

This commit is contained in:
dualfroz committed 2026-09-01 22:36:26 +02:00
1 parent 1a8cd39270
commit 785e147d44
1 file changed
+77
+77
View File
@@ -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);