test: cover onUsr1 and onUsr2
This commit is contained in:
1 parent
c0af392fd4
commit
3839c02887
1 file changed
+167
@@ -0,0 +1,167 @@
|
||||
#include "ulib.h"
|
||||
|
||||
/*
|
||||
* Boot-time, single-process live demo of sigFlagNoDefer (signalnum.h /
|
||||
* signal.c's sigConsumePending): unlike every other signal demo in this
|
||||
* kernel (nesttest/masktest/jobtest/...), this needs no second process --
|
||||
* SA_NODEFER is entirely about whether a signal can interrupt ITS OWN
|
||||
* still-active handler, a property one process can raise against itself
|
||||
* and observe directly, with no inter-process rendezvous required at all.
|
||||
*
|
||||
* Two contrasting handlers, proven in the same run:
|
||||
*
|
||||
* onUsr1 is registered WITH sigFlagNoDefer. Its first invocation
|
||||
* self-raises sigUsr1 again (userSigSend(selfPid, sigUsr1)) while still
|
||||
* active. Since sigFlagNoDefer means sigUsr1's own bit was never added
|
||||
* to sigBlocked, that re-raise is genuinely deliverable the instant the
|
||||
* self-raise syscall itself returns to ring 3 (isr.c's
|
||||
* deliverPendingSignals, unconditional on every trap return) -- entering
|
||||
* a SECOND, NESTED instance of onUsr1 before the first one's own
|
||||
* userSigSend call ever returns to it. The nested instance sees
|
||||
* usr1EnterCount already 2 and does not re-raise again (bounded
|
||||
* recursion), returns via the trampoline's automatic sysSigReturn,
|
||||
* letting the outer instance resume exactly where its self-raise call
|
||||
* left off and observe the count already at 2 -- proof the nesting
|
||||
* already happened, not a race it merely hopes for.
|
||||
*
|
||||
* onUsr2 is registered WITHOUT the flag (plain default, like every other
|
||||
* handler in this kernel before this session). Its first invocation
|
||||
* self-raises sigUsr2 the same way, but sigUsr2's own bit IS in
|
||||
* sigBlocked for as long as onUsr2 is active, so that re-raise's own
|
||||
* syscall returns without anything nesting -- onUsr2 finishes its first
|
||||
* invocation having observed no nested re-entry at all (the negative
|
||||
* half of the proof), and only once it actually returns (sysSigReturn
|
||||
* restoring sigBlocked without sigUsr2's self-block, isrDispatch
|
||||
* immediately re-checking pending signals on that very trap return,
|
||||
* exactly like masktarget.c's own deferred-delivery proof) does the
|
||||
* deferred second raise finally arrive, as its own fresh TOP-LEVEL
|
||||
* invocation, never nested.
|
||||
*
|
||||
* No sleep/timing guess anywhere: every self-raise's pending-bit write
|
||||
* happens synchronously inside the syscall against this exact same
|
||||
* thread, and delivery/deferral is entirely decided by sigBlocked at the
|
||||
* next trap return, which happens deterministically at each userSigSend's
|
||||
* or handler-return's own int 0x80 -- there is no window for either
|
||||
* outcome to be a coincidence of scheduling.
|
||||
*/
|
||||
|
||||
static long selfPid = -1;
|
||||
|
||||
static volatile int usr1EnterCount = 0;
|
||||
static volatile int usr1NestedConfirmed = 0;
|
||||
|
||||
static void onUsr1(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
usr1EnterCount++;
|
||||
|
||||
if (usr1EnterCount == 1) {
|
||||
print("nodefertest: onUsr1 first entry, self-raising sigUsr1 (NODEFER)\n");
|
||||
if (userSigSend(selfPid, sigUsr1) != 0) {
|
||||
print("nodefertest: FAIL self-raise sigUsr1\n");
|
||||
return;
|
||||
}
|
||||
/* If sigFlagNoDefer genuinely lifted the self-block, the nested
|
||||
instance of THIS SAME function already ran to completion, in
|
||||
full, during the userSigSend call above (its own return-to-ring3
|
||||
delivered it before ever coming back here) -- so usr1EnterCount
|
||||
must already read 2 by the time control resumes at this exact
|
||||
line, not merely "eventually". */
|
||||
if (usr1EnterCount >= 2) {
|
||||
usr1NestedConfirmed = 1;
|
||||
print("nodefertest: onUsr1 observed its own nested re-entry (nodefer-self-nest-ok)\n");
|
||||
} else {
|
||||
print("nodefertest: FAIL onUsr1 did not nest into itself\n");
|
||||
}
|
||||
} else {
|
||||
print("nodefertest: onUsr1 nested entry (depth 2)\n");
|
||||
}
|
||||
}
|
||||
|
||||
static volatile int usr2EnterCount = 0;
|
||||
static volatile int usr2WronglyNested = 0;
|
||||
|
||||
static void onUsr2(int signum)
|
||||
{
|
||||
(void)signum;
|
||||
usr2EnterCount++;
|
||||
|
||||
if (usr2EnterCount == 1) {
|
||||
print("nodefertest: onUsr2 first entry, self-raising sigUsr2 (default defer)\n");
|
||||
if (userSigSend(selfPid, sigUsr2) != 0) {
|
||||
print("nodefertest: FAIL self-raise sigUsr2\n");
|
||||
return;
|
||||
}
|
||||
/* Unlike onUsr1 above: sigUsr2's own bit IS self-blocked here (no
|
||||
sigFlagNoDefer), so the re-raise above must NOT have nested --
|
||||
usr2EnterCount must still read exactly 1 at this exact line. */
|
||||
if (usr2EnterCount != 1) {
|
||||
usr2WronglyNested = 1;
|
||||
print("nodefertest: FAIL onUsr2 nested into itself without sigFlagNoDefer\n");
|
||||
} else {
|
||||
print("nodefertest: onUsr2 confirmed no self-nest while deferred (still-deferred-ok)\n");
|
||||
}
|
||||
} else {
|
||||
print("nodefertest: onUsr2 second, TOP-LEVEL entry after first returned\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
print("nodefertest: start, pid=");
|
||||
printDec(userGetpid());
|
||||
print("\n");
|
||||
selfPid = userGetpid();
|
||||
|
||||
if (userSigAction(sigUsr1, onUsr1, 0, sigFlagNoDefer) == (long)sigSetFailed ||
|
||||
userSigAction(sigUsr2, onUsr2, 0, 0) == (long)sigSetFailed) {
|
||||
print("nodefertest: FAIL install handlers\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This syscall's own return-to-ring3 is what delivers onUsr1's FIRST
|
||||
invocation -- the nested second one (see onUsr1's own comment)
|
||||
happens entirely inside that delivery, synchronously, before this
|
||||
call ever returns to main() below. */
|
||||
if (userSigSend(selfPid, sigUsr1) != 0) {
|
||||
print("nodefertest: FAIL send sigUsr1 to self\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!usr1NestedConfirmed) {
|
||||
print("nodefertest: FAIL usr1 nodefer self-nest never confirmed\n");
|
||||
return 1;
|
||||
}
|
||||
if (usr1EnterCount != 2) {
|
||||
print("nodefertest: FAIL expected exactly 2 usr1 entries, got ");
|
||||
printDec(usr1EnterCount);
|
||||
print("\n");
|
||||
return 1;
|
||||
}
|
||||
print("nodefertest: nodefer-self-nest-verified-ok\n");
|
||||
|
||||
/* Same shape for onUsr2: this call's return delivers its first
|
||||
invocation; the DEFERRED second one (no nesting) is delivered on
|
||||
that first invocation's own sysSigReturn, still inside this single
|
||||
userSigSend's worth of ring-3 return handling, before control ever
|
||||
reaches back here. */
|
||||
if (userSigSend(selfPid, sigUsr2) != 0) {
|
||||
print("nodefertest: FAIL send sigUsr2 to self\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (usr2WronglyNested) {
|
||||
print("nodefertest: FAIL usr2 self-nested despite no sigFlagNoDefer\n");
|
||||
return 1;
|
||||
}
|
||||
if (usr2EnterCount != 2) {
|
||||
print("nodefertest: FAIL expected exactly 2 usr2 entries (deferred, not nested), got ");
|
||||
printDec(usr2EnterCount);
|
||||
print("\n");
|
||||
return 1;
|
||||
}
|
||||
print("nodefertest: default-defer-no-self-nest-verified-ok\n");
|
||||
|
||||
print("nodefertest: sa-nodefer-contrast-ok\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user