test: extend coverage of sigTest
This commit is contained in:
1 parent
0419e31b12
commit
a4a523ed11
1 file changed
+150
+150
@@ -1095,3 +1095,153 @@ KTEST(sigConsumePendingLeavesRsiRdxUntouchedWithoutSigFlagSigInfo)
|
|||||||
int signum = -1;
|
int signum = -1;
|
||||||
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
|
||||||
|
/* Untouched, exactly as documented -- a handler with no
|
||||||
|
sigFlagSigInfo never reads either register, so sigConsumePending
|
||||||
|
must not spend any effort computing values for them. */
|
||||||
|
KT_EQU(t, regs.rsi, 0x5151515151515151ull);
|
||||||
|
KT_EQU(t, regs.rdx, 0x6262626262626262ull);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaisePlainWrapperDefaultsToSiCodeKernelWithNoSenderOrStatus)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigInfoDefaultTarget", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
|
||||||
|
sigSetHandler(thread, sigHup, 0x40009000ull, 0, sigFlagSigInfo);
|
||||||
|
/* The plain, pre-existing sigRaise -- every one of this file's other
|
||||||
|
~50 call sites uses exactly this, and none of them supply detail,
|
||||||
|
so sigRaise's own thin-wrapper default (siCodeKernel/0/0) is what
|
||||||
|
they all still get. */
|
||||||
|
KT_TRUE(t, sigRaise(id, sigHup));
|
||||||
|
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
int signum = -1;
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
|
||||||
|
SigInfo* info = (SigInfo*)regs.rsi;
|
||||||
|
KT_EQ(t, info->siSignum, sigHup);
|
||||||
|
KT_EQ(t, info->siCode, siCodeKernel);
|
||||||
|
KT_EQ(t, info->siPid, 0);
|
||||||
|
KT_EQ(t, info->siStatus, 0);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseInfoOverwritesStalePendingInfoOnReRaiseBeforeDelivery)
|
||||||
|
{
|
||||||
|
uint32 id = threadCreate("sigInfoOverwriteTarget", noopThreadEntry);
|
||||||
|
KT_NE(t, id, threadInvalidId);
|
||||||
|
Thread* thread = threadAt(id);
|
||||||
|
|
||||||
|
sigSetHandler(thread, sigUsr2, 0x40009000ull, 0, sigFlagSigInfo);
|
||||||
|
KT_TRUE(t, sigRaiseInfo(id, sigUsr2, siCodeUser, 111, 1));
|
||||||
|
/* Same signum raised again before it was ever delivered -- non-queuing
|
||||||
|
signals coalesce, so only the SECOND raise's detail should ever be
|
||||||
|
observable, matching sigPending itself staying a single bit rather
|
||||||
|
than a count. */
|
||||||
|
KT_TRUE(t, sigRaiseInfo(id, sigUsr2, siCodeUser, 222, 2));
|
||||||
|
|
||||||
|
Registers regs = makeUserRegs();
|
||||||
|
int signum = -1;
|
||||||
|
KT_EQ(t, sigConsumePending(thread, ®s, &signum), sigOutcomeHandler);
|
||||||
|
|
||||||
|
SigInfo* info = (SigInfo*)regs.rsi;
|
||||||
|
KT_EQ(t, info->siPid, 222);
|
||||||
|
KT_EQ(t, info->siStatus, 2);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigRaiseGroup (signal.h) -- the group-targeted half of real kill(2)'s
|
||||||
|
* pid<0/pid==0 convention (syscall.c's sysSigSend) and keyboard.c's own
|
||||||
|
* real Ctrl-C/Ctrl-Z, delivered to the FOREGROUND process group
|
||||||
|
* (sched.h's Thread.pgid) rather than one hardcoded target. The live
|
||||||
|
* ring-3 proof that this scoping actually protects a DIFFERENT group from
|
||||||
|
* a keyboard-generated signal is user/pgroupfg.c (killed by it) versus
|
||||||
|
* user/pgroupbg.c (proven still alive afterwards) in kmain.c's boot
|
||||||
|
* sequence; what is deterministic here is the plain fan-out-and-filter
|
||||||
|
* logic itself, mirroring schedTest.c's threadKillGroup coverage for the
|
||||||
|
* exact same shape of primitive one layer down.
|
||||||
|
*/
|
||||||
|
|
||||||
|
KTEST(sigRaiseGroupDeliversToEveryLiveMemberOfThatGroupOnly)
|
||||||
|
{
|
||||||
|
uint32 a = threadCreate("sigGroupA", noopThreadEntry);
|
||||||
|
uint32 b = threadCreate("sigGroupB", noopThreadEntry);
|
||||||
|
uint32 outsider = threadCreate("sigGroupOutsider", noopThreadEntry);
|
||||||
|
KT_NE(t, a, threadInvalidId);
|
||||||
|
KT_NE(t, b, threadInvalidId);
|
||||||
|
KT_NE(t, outsider, threadInvalidId);
|
||||||
|
|
||||||
|
uint32 pgid = threadAt(a)->pgid;
|
||||||
|
KT_TRUE(t, threadSetPgid(b, pgid));
|
||||||
|
KT_NE(t, threadAt(outsider)->pgid, pgid);
|
||||||
|
|
||||||
|
KT_TRUE(t, sigRaiseGroup(pgid, sigUsr1, siCodeKernel, 0));
|
||||||
|
KT_EQU(t, threadAt(a)->sigPending & (1ull << sigUsr1), (1ull << sigUsr1));
|
||||||
|
KT_EQU(t, threadAt(b)->sigPending & (1ull << sigUsr1), (1ull << sigUsr1));
|
||||||
|
/* The whole point: a DIFFERENT group must be completely untouched --
|
||||||
|
this is what keeps a real Ctrl-C from ever reaching a background
|
||||||
|
job. */
|
||||||
|
KT_EQU(t, threadAt(outsider)->sigPending, 0ull);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(a));
|
||||||
|
KT_TRUE(t, threadDiscard(b));
|
||||||
|
KT_TRUE(t, threadDiscard(outsider));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseGroupReturnsFalseWhenNoLiveThreadHasThatPgid)
|
||||||
|
{
|
||||||
|
KT_FALSE(t, sigRaiseGroup(0xBEEF0001u, sigUsr1, siCodeKernel, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseGroupSkipsDeadOrUnusedMembersButStillFindsLiveOnes)
|
||||||
|
{
|
||||||
|
uint32 a = threadCreate("sigGroupDeadA", noopThreadEntry);
|
||||||
|
uint32 b = threadCreate("sigGroupDeadB", noopThreadEntry);
|
||||||
|
KT_NE(t, a, threadInvalidId);
|
||||||
|
KT_NE(t, b, threadInvalidId);
|
||||||
|
|
||||||
|
uint32 pgid = threadAt(a)->pgid;
|
||||||
|
KT_TRUE(t, threadSetPgid(b, pgid));
|
||||||
|
KT_TRUE(t, threadKill(b));
|
||||||
|
|
||||||
|
KT_TRUE(t, sigRaiseGroup(pgid, sigUsr2, siCodeKernel, 0));
|
||||||
|
KT_EQU(t, threadAt(a)->sigPending & (1ull << sigUsr2), (1ull << sigUsr2));
|
||||||
|
/* Dead members are silently skipped, exactly like sigRaiseInfo's own
|
||||||
|
Dead/Unused guard already refuses a single-target send against one
|
||||||
|
-- no crash, no stale sigPending bit written into a slot that is no
|
||||||
|
longer this thread's own. */
|
||||||
|
KT_EQU(t, threadAt(b)->sigPending, 0ull);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadDiscard(a));
|
||||||
|
KT_TRUE(t, threadReap(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
KTEST(sigRaiseGroupWithSigStopStopsEveryLiveMemberOfThatGroup)
|
||||||
|
{
|
||||||
|
uint32 a = threadCreate("sigGroupStopA", noopThreadEntry);
|
||||||
|
uint32 b = threadCreate("sigGroupStopB", noopThreadEntry);
|
||||||
|
KT_NE(t, a, threadInvalidId);
|
||||||
|
KT_NE(t, b, threadInvalidId);
|
||||||
|
|
||||||
|
uint32 pgid = threadAt(a)->pgid;
|
||||||
|
KT_TRUE(t, threadSetPgid(b, pgid));
|
||||||
|
|
||||||
|
/* sigStop is delegated straight to threadStop per-target, bypassing
|
||||||
|
sigPending entirely -- the same bypass sigRaiseInfo's own file
|
||||||
|
comment documents for a single-target sigStop, now proven to apply
|
||||||
|
independently to every member the group fan-out visits. */
|
||||||
|
KT_TRUE(t, sigRaiseGroup(pgid, sigStop, siCodeKernel, 0));
|
||||||
|
KT_EQ(t, threadAt(a)->state, threadStopped);
|
||||||
|
KT_EQ(t, threadAt(b)->state, threadStopped);
|
||||||
|
|
||||||
|
KT_TRUE(t, threadContinue(a));
|
||||||
|
KT_TRUE(t, threadContinue(b));
|
||||||
|
KT_TRUE(t, threadDiscard(a));
|
||||||
|
KT_TRUE(t, threadDiscard(b));
|
||||||
|
}
|
||||||
Reference in new issue
Block a user