feat: continue work on sched
This commit is contained in:
1 parent
9d356d9a29
commit
51fcf54f5b
1 file changed
+57
-10
+57
-10
@@ -575,31 +575,77 @@ uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop)
|
|||||||
return slot;
|
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)
|
uint64 schedule(uint64 currentRsp)
|
||||||
{
|
{
|
||||||
if (!schedulerActive)
|
if (!schedulerActive)
|
||||||
return currentRsp;
|
return currentRsp;
|
||||||
|
|
||||||
threads[currentIndex].rsp = currentRsp;
|
Thread* current = &threads[currentIndex];
|
||||||
if (threads[currentIndex].state == threadRunning)
|
current->rsp = currentRsp;
|
||||||
threads[currentIndex].state = threadRunnable;
|
if (current->state == threadRunning) {
|
||||||
|
current->vruntime += schedTickCharge * schedDefaultWeight / current->weight;
|
||||||
|
current->state = threadRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
uint64 now = pitTicks();
|
uint64 now = pitTicks();
|
||||||
for (uint32 i = 0; i < threadTotal; i++)
|
for (uint32 i = 0; i < threadTotal; i++)
|
||||||
if (threads[i].state == threadSleeping && now >= threads[i].sleepUntil)
|
if (threads[i].state == threadSleeping && now >= threads[i].sleepUntil)
|
||||||
threads[i].state = threadRunnable;
|
threads[i].state = threadRunnable;
|
||||||
|
|
||||||
uint32 next = 0;
|
/* sysPoll's own timeout, forced the exact same way sleepUntil's expiry
|
||||||
for (uint32 step = 1; step <= threadTotal; step++) {
|
is above: a thread parked on schedPollChannel because none of its
|
||||||
uint32 candidate = (currentIndex + step) % threadTotal;
|
requested fds were ready yet still needs to wake up on its own even
|
||||||
if (threads[candidate].state == threadRunnable) {
|
if NONE of them ever becomes ready (pollDeadline == 0 means "wait
|
||||||
next = candidate;
|
forever", i.e. a real timeout of -1, and is deliberately never
|
||||||
break;
|
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;
|
currentIndex = next;
|
||||||
threads[next].state = threadRunning;
|
threads[next].state = threadRunning;
|
||||||
|
threads[next].everRan = true;
|
||||||
tssSetRsp0(threads[next].kernelStackTop);
|
tssSetRsp0(threads[next].kernelStackTop);
|
||||||
return threads[next].rsp;
|
return threads[next].rsp;
|
||||||
}
|
}
|
||||||
@@ -615,6 +661,7 @@ void schedStart(void)
|
|||||||
void threadExit(uint64 code)
|
void threadExit(uint64 code)
|
||||||
{
|
{
|
||||||
Thread* thread = &threads[currentIndex];
|
Thread* thread = &threads[currentIndex];
|
||||||
|
thread->exitCode = code;
|
||||||
thread->state = threadDead;
|
thread->state = threadDead;
|
||||||
kprintf("[user] pid=%u '%s' exited with code %lu\n", thread->id, thread->name, code);
|
kprintf("[user] pid=%u '%s' exited with code %lu\n", thread->id, thread->name, code);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user