test: extend coverage of noopThreadEntry

This commit is contained in:
dualfroz committed 2026-09-11 12:58:20 +02:00
1 parent 7c2a04ceb4
commit d4cdde3ea0
1 file changed
+151
+151
View File
@@ -0,0 +1,151 @@
#include "ktest.h"
#include "sched.h"
#include "ports.h"
#include "elf.h"
#include "paging.h"
#include "pmm.h"
#include "pipe.h"
#include "signal.h"
#include "pit.h"
#include "klib.h"
/*
* ktestRunAll() runs before schedStart() (see ktest.h), on a single thread
* with no live scheduler - there is no second execution context to actually
* context-switch into, and no interrupts to wake a genuinely halted CPU.
* The first block of tests below therefore exercises schedBlockCurrent/
* schedWakeChannel as the pure state-machine they are (mark-blocked /
* match-and-wake), using the always-present current thread (idle, id 0 at
* this point in boot) rather than creating new threads.
*
* The second block tests threadCreate/threadReap/threadJoin's bookkeeping
* (slot allocation, exhaustion, reuse, bounds-checking) directly: a thread
* created here is never actually scheduled to run (the scheduler is not yet
* live), so as long as every thread a test creates is reaped again before
* the test returns, the real thread table is left exactly as it found it -
* no throwaway slot is ever left behind to eat into kmain.c's later budget.
* The one path that genuinely cannot be tested here is threadJoin() ever
* actually parking (schedBlockCurrent + halt()) on a thread that is still
* alive: that requires a second thread for the scheduler to switch to,
* which does not exist before schedStart(). That path is instead proven
* live by the join demo in kmain.c (needle "join-reap-ok" in ci-test.sh).
*/
KTEST(schedBlockCurrentMarksBlockedWithChannel)
{
ThreadState saved = currentThread()->state;
int channel;
schedBlockCurrent(&channel);
KT_EQ(t, currentThread()->state, threadBlocked);
KT_EQP(t, currentThread()->waitChannel, &channel);
schedWakeChannel(&channel);
currentThread()->state = saved;
}
KTEST(schedWakeChannelIgnoresMismatchedChannel)
{
ThreadState saved = currentThread()->state;
int channelA;
int channelB;
schedBlockCurrent(&channelA);
KT_EQ(t, currentThread()->state, threadBlocked);
schedWakeChannel(&channelB);
KT_EQ(t, currentThread()->state, threadBlocked);
KT_EQP(t, currentThread()->waitChannel, &channelA);
schedWakeChannel(&channelA);
KT_EQ(t, currentThread()->state, threadRunnable);
currentThread()->state = saved;
}
KTEST(schedWakeChannelClearsWaitChannelOnWake)
{
ThreadState saved = currentThread()->state;
int channel;
schedBlockCurrent(&channel);
schedWakeChannel(&channel);
KT_EQ(t, currentThread()->state, threadRunnable);
KT_NULL(t, currentThread()->waitChannel);
currentThread()->state = saved;
}
KTEST(schedPollChannelIsStableAndUsableLikeAnyOtherChannel)
{
ThreadState saved = currentThread()->state;
/* Same address every call -- every not-yet-satisfied sysPoll (poll.h)
across every thread blocks on this ONE shared channel, so callers
must never see it move. */
KT_EQP(t, schedPollChannel(), schedPollChannel());
KT_NOTNULL(t, schedPollChannel());
/* And it works exactly like any other channel schedBlockCurrent/
schedWakeChannel already accept -- the schedule()-driven pollDeadline
expiry scan (sched.c) that forces a wake even with nobody calling
schedWakeChannel(schedPollChannel()) needs a live scheduler
(schedulerActive) to run at all, so it is proven live instead, by
user/polltest.c's real timeout phase (tools/ci-test.sh's
"polltest: poll-timeout-ok" needle) -- the same split schedTest.c's
own file comment already documents for sleepUntil/alarmAt. */
schedBlockCurrent(schedPollChannel());
KT_EQ(t, currentThread()->state, threadBlocked);
KT_EQP(t, currentThread()->waitChannel, schedPollChannel());
schedWakeChannel(schedPollChannel());
KT_EQ(t, currentThread()->state, threadRunnable);
currentThread()->state = saved;
}
KTEST(schedWakeChannelWithNoWaiterIsHarmlessNoop)
{
ThreadState saved = currentThread()->state;
int channel;
/* Nobody is blocked on this address: waking it must not touch any
thread's state, including the caller's own. */
schedWakeChannel(&channel);
KT_EQ(t, currentThread()->state, saved);
currentThread()->state = saved;
}
KTEST(schedBlockCurrentIsIdempotentUnderRepeatedCalls)
{
ThreadState saved = currentThread()->state;
int channelA;
int channelB;
schedBlockCurrent(&channelA);
schedBlockCurrent(&channelB);
KT_EQ(t, currentThread()->state, threadBlocked);
KT_EQP(t, currentThread()->waitChannel, &channelB);
/* Waking the stale first channel must not affect a thread that has since
re-blocked on a different one. */
schedWakeChannel(&channelA);
KT_EQ(t, currentThread()->state, threadBlocked);
schedWakeChannel(&channelB);
KT_EQ(t, currentThread()->state, threadRunnable);
currentThread()->state = saved;
}
/* Never actually invoked (the scheduler is not live yet) -- just a valid
function pointer for threadCreate's entry parameter. If it were ever
scheduled for real it would be a harmless, correctly-behaved kernel
thread body (park forever), same convention as workerA/workerB. */
static void noopThreadEntry(void)
{
for (;;)
halt();
}