test: test more of schedTest
This commit is contained in:
1 parent
faaf04fc8c
commit
c31ac87199
1 file changed
+158
+158
@@ -784,3 +784,161 @@ KTEST(threadStopAcceptsSleepingBlockedAndRunningStates)
|
||||
uint32 id = threadCreate("stopFromEveryState", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
|
||||
/* Fabricated states, the same way threadReapClosesOpenWritePipeFdSoReaderSeesEof
|
||||
below pokes threadDead directly: this thread is never actually
|
||||
scheduled in this pre-schedStart() harness, so its `state` field
|
||||
stands in for "genuinely Sleeping/Blocked/Running" -- exactly the
|
||||
states a real SIGSTOP target could be caught in. */
|
||||
threadAt(id)->state = threadSleeping;
|
||||
KT_TRUE(t, threadStop(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||
KT_TRUE(t, threadContinue(id));
|
||||
|
||||
threadAt(id)->state = threadBlocked;
|
||||
KT_TRUE(t, threadStop(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||
KT_TRUE(t, threadContinue(id));
|
||||
|
||||
/* Running -- the self-stop case (sysSigSend targeting the caller's own
|
||||
pid, syscall.c): threadStop does not special-case "am I the one
|
||||
calling this" the way threadKill implicitly does (Running is simply
|
||||
absent from threadKill's accepted set); a thread can stop itself. */
|
||||
threadAt(id)->state = threadRunning;
|
||||
KT_TRUE(t, threadStop(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||
|
||||
KT_TRUE(t, threadContinue(id));
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
KTEST(threadStopOnDeadOrUnusedSlotFails)
|
||||
{
|
||||
uint32 id = threadCreate("stopDead", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
|
||||
threadAt(id)->state = threadDead;
|
||||
KT_FALSE(t, threadStop(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadDead);
|
||||
KT_TRUE(t, threadReap(id));
|
||||
|
||||
/* threadUnused again after reap: still rejected. */
|
||||
KT_FALSE(t, threadStop(id));
|
||||
}
|
||||
|
||||
KTEST(threadContinueRejectsOutOfRangeAndNonStoppedThreads)
|
||||
{
|
||||
KT_FALSE(t, threadContinue(threadCapacity()));
|
||||
KT_FALSE(t, threadContinue(threadCapacity() + 1000));
|
||||
|
||||
uint32 id = threadCreate("notStopped", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
|
||||
/* Runnable, never stopped: SIGCONT against a process that was never
|
||||
stopped is a harmless no-op at this level (see signal.c's
|
||||
sigRaise, which still delivers the signal itself regardless). */
|
||||
KT_FALSE(t, threadContinue(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadRunnable);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
KTEST(threadStopExcludesTargetFromUnrelatedWaitChannelWake)
|
||||
{
|
||||
int channel;
|
||||
|
||||
uint32 id = threadCreate("stoppedWhileWaiting", noopThreadEntry);
|
||||
KT_NE(t, id, threadInvalidId);
|
||||
|
||||
/* Fabricate exactly what schedBlockCurrent would have left behind had
|
||||
this thread been the one calling it (see
|
||||
threadDiscardClosesOpenReadPipeFdSoWriterSeesBrokenPipe below for
|
||||
the same fabricate-then-assert convention). */
|
||||
threadAt(id)->state = threadBlocked;
|
||||
threadAt(id)->waitChannel = &channel;
|
||||
|
||||
KT_TRUE(t, threadStop(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||
|
||||
/* The defining difference from threadBlocked: schedWakeChannel must
|
||||
NOT match a Stopped thread even though its waitChannel field still
|
||||
names this exact channel -- only threadContinue (an explicit
|
||||
SIGCONT) may move it back to threadRunnable, never "the resource it
|
||||
was originally waiting on became available". */
|
||||
schedWakeChannel(&channel);
|
||||
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||
|
||||
KT_TRUE(t, threadContinue(id));
|
||||
KT_EQ(t, threadAt(id)->state, threadRunnable);
|
||||
|
||||
KT_TRUE(t, threadDiscard(id));
|
||||
}
|
||||
|
||||
/*
|
||||
* threadStop/threadContinue's own new SIGCHLD-on-stop/SIGCHLD-on-continue
|
||||
* (sched.c, mirroring threadExit/threadKill's parentId/sigChld block
|
||||
* above): real job control notifies the parent asynchronously the instant
|
||||
* a child stops or resumes, not only when it eventually dies. The live
|
||||
* ring-3 proof that this reaches a genuinely blocked-mid-syscall target
|
||||
* and a real installed handler is user/stopchldtest.c spawning
|
||||
* user/stopchldtarget.c (needle "stopchldtest: job-control-sigchld-ok" in
|
||||
* tools/ci-test.sh); what is deterministic here is the same plumbing shape
|
||||
* threadKillRaisesSigChldAgainstParentId/
|
||||
* threadKillEarlyWakesAParentGenuinelyBlockedWaitingForSigChld above
|
||||
* already establish for the death case.
|
||||
*/
|
||||
|
||||
KTEST(threadStopRaisesSigChldAgainstParentIdOnTheActualStopTransition)
|
||||
{
|
||||
uint32 parent = threadCreate("stopChldParent", noopThreadEntry);
|
||||
KT_NE(t, parent, threadInvalidId);
|
||||
uint32 child = threadCreate("stopChldVictim", noopThreadEntry);
|
||||
KT_NE(t, child, threadInvalidId);
|
||||
threadAt(child)->parentId = parent;
|
||||
|
||||
KT_EQU(t, threadAt(parent)->sigPending & (1ull << sigChld), 0ull);
|
||||
KT_TRUE(t, threadStop(child));
|
||||
KT_EQU(t, threadAt(parent)->sigPending & (1ull << sigChld), (1ull << sigChld));
|
||||
|
||||
KT_TRUE(t, threadContinue(child));
|
||||
KT_TRUE(t, threadDiscard(child));
|
||||
KT_TRUE(t, threadDiscard(parent));
|
||||
}
|
||||
|
||||
KTEST(threadStopDoesNotReRaiseSigChldAgainstAnAlreadyStoppedTarget)
|
||||
{
|
||||
uint32 parent = threadCreate("stopChldParent2", noopThreadEntry);
|
||||
KT_NE(t, parent, threadInvalidId);
|
||||
uint32 child = threadCreate("stopChldVictim2", noopThreadEntry);
|
||||
KT_NE(t, child, threadInvalidId);
|
||||
threadAt(child)->parentId = parent;
|
||||
|
||||
KT_TRUE(t, threadStop(child));
|
||||
KT_EQU(t, threadAt(parent)->sigPending & (1ull << sigChld), (1ull << sigChld));
|
||||
|
||||
/* Simulate the parent's own handler already having consumed that one
|
||||
notification (sigConsumePending, signal.c, clears the bit on
|
||||
delivery) -- exactly the "next natural ring-3 return" a real
|
||||
sigchldtest.c-style handler would have already run through before a
|
||||
second, redundant sigStop against the same still-Stopped target
|
||||
could possibly arrive. */
|
||||
threadAt(parent)->sigPending &= ~(1ull << sigChld);
|
||||
|
||||
KT_TRUE(t, threadStop(child));
|
||||
KT_EQ(t, threadAt(child)->state, threadStopped);
|
||||
KT_EQU(t, threadAt(parent)->sigPending & (1ull << sigChld), 0ull);
|
||||
|
||||
KT_TRUE(t, threadContinue(child));
|
||||
KT_TRUE(t, threadDiscard(child));
|
||||
KT_TRUE(t, threadDiscard(parent));
|
||||
}
|
||||
|
||||
KTEST(threadStopEarlyWakesAParentGenuinelyBlockedWaitingForSigChldOnStop)
|
||||
{
|
||||
int channel;
|
||||
|
||||
uint32 parent = threadCreate("stopChldBlockedParent", noopThreadEntry);
|
||||
KT_NE(t, parent, threadInvalidId);
|
||||
uint32 child = threadCreate("stopChldVictim3", noopThreadEntry);
|
||||
KT_NE(t, child, threadInvalidId);
|
||||
threadAt(child)->parentId = parent;
|
||||
|
||||
Reference in new issue
Block a user