test: cover sigTest
This commit is contained in:
1 parent
cc2dda7d08
commit
4740ca0a05
1 file changed
+157
+157
@@ -155,3 +155,160 @@ KTEST(sigRaiseSigContOnNonStoppedTargetStillDeliversSignal)
|
|||||||
uint32 id = threadCreate("sigContNotStopped", noopThreadEntry);
|
uint32 id = threadCreate("sigContNotStopped", noopThreadEntry);
|
||||||
KT_NE(t, id, threadInvalidId);
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
|
||||||
|
/* Never stopped: SIGCONT here is a no-op at the process-state level
|
||||||
|
(real Unix: continuing an already-running process does nothing extra),
|
||||||
|
but the signal itself is still delivered -- an installed handler
|
||||||
|
still runs through the normal sigConsumePending scan regardless. */
|
||||||
|
KT_TRUE(t, sigRaise(id, sigCont));
|
||||||
|
KT_EQ(t, threadAt(id)->state, threadRunnable);
|
||||||
|
KT_TRUE(t, threadAt(id)->sigPending & (1ull << sigCont));
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseWakesSleepingOrBlockedTargetEarlyForADeliverableSignal)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigWakeSleeping", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
|
||||||
|
/* No handler installed: sigTerm's default disposition is Terminate,
|
||||||
|
which sigWouldDeliver treats as genuinely deliverable -- exactly the
|
||||||
|
user/eintrtarget.c scenario (a thread blocked on a condition that
|
||||||
|
will never naturally occur, recovered by an ORDINARY signal, not
|
||||||
|
threadKill's direct bypass). */
|
||||||
|
thread->state = threadSleeping;
|
||||||
|
KT_TRUE(t, sigRaise(id, sigTerm));
|
||||||
|
KT_EQ(t, thread->state, threadRunnable);
|
||||||
|
|
||||||
|
thread->state = threadBlocked;
|
||||||
|
KT_TRUE(t, sigRaise(id, sigTstp));
|
||||||
|
KT_EQ(t, thread->state, threadRunnable);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseDoesNotWakeSleepingOrBlockedTargetForABlockedOrIgnoredSignal)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigNoWakeBlocked", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
|
||||||
|
/* sigUsr1 explicitly blocked: must not disturb the sleep at all, same
|
||||||
|
as real Unix -- a blocked signal cannot interrupt anything. */
|
||||||
|
sigProcMask(thread, sigMaskBlock, (1ull << sigUsr1));
|
||||||
|
thread->state = threadSleeping;
|
||||||
|
KT_TRUE(t, sigRaise(id, sigUsr1));
|
||||||
|
KT_EQ(t, thread->state, threadSleeping);
|
||||||
|
KT_TRUE(t, thread->sigPending & (1ull << sigUsr1));
|
||||||
|
|
||||||
|
/* sigChld's real-world default is Ignore -- dismissed outright, never
|
||||||
|
delivered, so it must not wake anything either. */
|
||||||
|
thread->state = threadBlocked;
|
||||||
|
KT_TRUE(t, sigRaise(id, sigChld));
|
||||||
|
KT_EQ(t, thread->state, threadBlocked);
|
||||||
|
|
||||||
|
/* An explicit sigHandlerIgnore on an otherwise-Terminate signal is the
|
||||||
|
same story. */
|
||||||
|
sigSetHandler(thread, sigUsr2, sigHandlerIgnore, 0, 0);
|
||||||
|
KT_TRUE(t, sigRaise(id, sigUsr2));
|
||||||
|
KT_EQ(t, thread->state, threadBlocked);
|
||||||
|
|
||||||
|
threadAt(id)->state = threadRunnable;
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseDoesNotEarlyWakeForSignalMaskedByActiveHandlersSaMask)
|
||||||
|
{
|
||||||
|
/* The gap the two tests above do NOT cover: sigWouldDeliver (and
|
||||||
|
therefore threadWakeForSignal) reads thread->sigBlocked verbatim,
|
||||||
|
with no idea whether any given bit got there via an explicit
|
||||||
|
sigProcMask call (tested above) or via sigConsumePending's own
|
||||||
|
handler-entry union of sigHandlerMasks[signum] (the file comment's
|
||||||
|
per-signal sa_mask paragraph). This drives the SECOND path for real
|
||||||
|
-- actually entering a handler via sigConsumePending, not just
|
||||||
|
calling sigProcMask by hand -- to prove the early-wake mechanism
|
||||||
|
(FALA 16) genuinely respects a mask that arrived that way too, the
|
||||||
|
exact interaction FALA 19 flagged as unexamined before deferring
|
||||||
|
this whole topic. user/masktarget.c is this same scenario live: its
|
||||||
|
onUsr1 handler (sa_mask = sigHup's bit here, sigUsr2's there) blocks
|
||||||
|
on a real read WHILE the mask is active, so an early wake bug here
|
||||||
|
would hang it forever instead of just failing a check. */
|
||||||
|
uint32 id = threadCreate("sigMaskedNoEarlyWake", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
int signum = -1;
|
||||||
|
|
||||||
|
uint64 usr1Handler = 0x40009000ull;
|
||||||
|
sigSetHandler(thread, sigUsr1, usr1Handler, (1ull << sigHup), 0);
|
||||||
|
sigRaise(id, sigUsr1);
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
KT_TRUE(t, thread->sigBlocked & (1ull << sigHup));
|
||||||
|
|
||||||
|
/* onUsr1 is now "running" (as far as this thread's own state is
|
||||||
|
concerned) and immediately blocks on something real, exactly like
|
||||||
|
user/masktarget.c's own userRead(goFd) call from inside its
|
||||||
|
handler. */
|
||||||
|
thread->state = threadBlocked;
|
||||||
|
|
||||||
|
/* sigHup is covered by onUsr1's sa_mask -- sigWouldDeliver must say no,
|
||||||
|
so threadWakeForSignal is never even called: the Blocked state (and
|
||||||
|
therefore whatever real wait this models) must survive completely
|
||||||
|
undisturbed, same as the explicit-sigProcMask case above. */
|
||||||
|
KT_TRUE(t, sigRaise(id, sigHup));
|
||||||
|
KT_EQ(t, thread->state, threadBlocked);
|
||||||
|
KT_TRUE(t, thread->sigPending & (1ull << sigHup));
|
||||||
|
|
||||||
|
/* Once onUsr1 genuinely returns (sigOnSigReturn, restoring sigBlocked
|
||||||
|
to what it was before -- no sigHup bit), the deferred sigHup is
|
||||||
|
deliverable again, but this thread is STILL Blocked (the read it was
|
||||||
|
modelling has not itself completed) -- sigOnSigReturn only unmasks,
|
||||||
|
it is not itself a wake source, so nothing here should move the
|
||||||
|
thread back to Runnable behind the scheduler's own back. */
|
||||||
|
KT_TRUE(t, sigOnSigReturn(thread, ®s));
|
||||||
|
KT_FALSE(t, thread->sigBlocked & (1ull << sigHup));
|
||||||
|
KT_EQ(t, thread->state, threadBlocked);
|
||||||
|
|
||||||
|
threadAt(id)->state = threadRunnable;
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseNeverWakesAStoppedTargetForAnOrdinarySignal)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigStoppedNoWake", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadStop(id));
|
||||||
|
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||||
|
|
||||||
|
/* sigTerm would genuinely deliver against a Runnable/Sleeping/Blocked
|
||||||
|
target (sigWouldDeliver says yes -- no handler, default Terminate,
|
||||||
|
not blocked) but job control's "stopped means stopped" invariant
|
||||||
|
must survive it existing at all: only threadContinue (an explicit
|
||||||
|
SIGCONT) may ever move a Stopped thread, exactly like real Unix
|
||||||
|
(a stopped process does not die of SIGTERM either, until continued
|
||||||
|
or SIGKILLed). */
|
||||||
|
KT_TRUE(t, sigRaise(id, sigTerm));
|
||||||
|
KT_EQ(t, threadAt(id)->state, threadStopped);
|
||||||
|
KT_TRUE(t, threadAt(id)->sigPending & (1ull << sigTerm));
|
||||||
|
|
||||||
|
KT_TRUE(t, threadKill(id));
|
||||||
|
KT_TRUE(t, threadReap(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigSetHandlerInstallsAndReturnsPreviousDisposition)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigHandlerTarget", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
|
||||||
|
KT_EQU(t, sigSetHandler(thread, sigUsr1, 0x40005000ull, 0, 0), sigHandlerDefault);
|
||||||
|
KT_EQU(t, thread->sigHandlers[sigUsr1], 0x40005000ull);
|
||||||
|
|
||||||
|
KT_EQU(t, sigSetHandler(thread, sigUsr1, sigHandlerIgnore, 0, 0), 0x40005000ull);
|
||||||
|
KT_EQU(t, thread->sigHandlers[sigUsr1], sigHandlerIgnore);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user