feat: continue work on sched

This commit is contained in:
dualfroz committed 2026-09-06 21:32:26 +02:00
1 parent 9d356d9a29
commit 51fcf54f5b
1 file changed
+57 -10
+57 -10
View File
@@ -575,31 +575,77 @@ uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop)
return slot;
}
/* CFS-lite selection: every reschedule rebuilds a min-vruntime priority
queue (prioq.h, backed by the intrusive rbt) from every currently
Runnable thread and picks the minimum. The queue is deliberately
ephemeral (built fresh each call, discarded after one pop) rather than a
run-queue whose membership persists across calls -- a thread can become
Runnable from code paths other than schedule() itself (schedWakeChannel,
called from pipe.c on the producer/consumer side of a wait-queue), and
rebuilding from the authoritative `state` field every time means there is
never a second copy of "who is runnable" to fall out of sync. */
uint64 schedule(uint64 currentRsp)
{
if (!schedulerActive)
return currentRsp;
threads[currentIndex].rsp = currentRsp;
if (threads[currentIndex].state == threadRunning)
threads[currentIndex].state = threadRunnable;
Thread* current = &threads[currentIndex];
current->rsp = currentRsp;
if (current->state == threadRunning) {
current->vruntime += schedTickCharge * schedDefaultWeight / current->weight;
current->state = threadRunnable;
}
uint64 now = pitTicks();
for (uint32 i = 0; i < threadTotal; i++)
if (threads[i].state == threadSleeping && now >= threads[i].sleepUntil)
threads[i].state = threadRunnable;
uint32 next = 0;
for (uint32 step = 1; step <= threadTotal; step++) {
uint32 candidate = (currentIndex + step) % threadTotal;
if (threads[candidate].state == threadRunnable) {
next = candidate;
break;
}
/* sysPoll's own timeout, forced the exact same way sleepUntil's expiry
is above: a thread parked on schedPollChannel because none of its
requested fds were ready yet still needs to wake up on its own even
if NONE of them ever becomes ready (pollDeadline == 0 means "wait
forever", i.e. a real timeout of -1, and is deliberately never
forced here). The retried sysPoll call (syscall.c) re-checks every
fd from scratch on wake regardless of why it woke, so forcing
Runnable here without clearing waitChannel first (same as the
sleepUntil scan just above) is enough -- there is no separate
"why did I wake" state to reconcile. */
for (uint32 i = 0; i < threadTotal; i++)
if (threads[i].state == threadBlocked && threads[i].pollActive &&
threads[i].pollDeadline != 0 && now >= threads[i].pollDeadline)
threads[i].state = threadRunnable;
/* Real alarm(2) delivery: a deadline set by threadSetAlarm fires here,
once per timer tick, regardless of what state its owning thread is
currently in (Running, Sleeping, Blocked -- a real alarm keeps
ticking no matter what the process is doing). Cleared BEFORE
raising, not after, so one expiry fires sigAlrm exactly once, not
once per tick until something else clears it; sigRaiseInfo (signal.h)
already refuses a Dead/Unused target harmlessly, so a thread that
exited with an alarm still pending needs no special case here.
siCodeKernel/0/0: a real SIGALRM has no sending process and no exit
status of its own to report (real Unix's own SIGALRM si_code is
SI_KERNEL/SI_TIMER for exactly this reason -- it is the kernel's
timer, not another process, that raised it). */
for (uint32 i = 0; i < threadTotal; i++)
if (threads[i].alarmAt != 0 && now >= threads[i].alarmAt) {
threads[i].alarmAt = 0;
sigRaiseInfo(threads[i].id, sigAlrm, siCodeKernel, 0, 0);
}
PrioQueue ready;
prioqInit(&ready);
for (uint32 i = 0; i < threadTotal; i++)
if (threads[i].state == threadRunnable)
prioqInsert(&ready, &threads[i].schedNode, threads[i].vruntime, threads[i].id);
PrioQNode* winner = prioqPopMin(&ready);
uint32 next = winner ? winner->id : currentIndex;
currentIndex = next;
threads[next].state = threadRunning;
threads[next].everRan = true;
tssSetRsp0(threads[next].kernelStackTop);
return threads[next].rsp;
}
@@ -615,6 +661,7 @@ void schedStart(void)
void threadExit(uint64 code)
{
Thread* thread = &threads[currentIndex];
thread->exitCode = code;
thread->state = threadDead;
kprintf("[user] pid=%u '%s' exited with code %lu\n", thread->id, thread->name, code);
}