168 lines
6.8 KiB
C
168 lines
6.8 KiB
C
#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;
|
|
}
|