diff --git a/user/nodefertest.c b/user/nodefertest.c new file mode 100644 index 0000000..6acdaee --- /dev/null +++ b/user/nodefertest.c @@ -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; +}