test: extend coverage of noopThreadEntry
This commit is contained in:
1 parent
7c2a04ceb4
commit
d4cdde3ea0
1 file changed
+151
+151
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user