feat: introduce onUsr2 and onUsr1
This commit is contained in:
1 parent
412e4fd9a1
commit
9480af603f
2 files changed
+188
No files matched your search
@@ -0,0 +1,153 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only ever spawned by nesttest.c, with three shared pipe handles as argv
|
||||||
|
* (ready/handlerReady/go, decToString'd -- the same convention jobtest.c's
|
||||||
|
* two-pipe rendezvous already established, extended to three because this
|
||||||
|
* needs an extra one to announce "the handler has genuinely entered and is
|
||||||
|
* about to block" from INSIDE a signal handler, not main()).
|
||||||
|
*
|
||||||
|
* Proves real, ring-3 NESTED signal delivery end to end (signal.h's
|
||||||
|
* per-signal sa_mask / Thread.sigHandlerDepth): onUsr1 installs no sa_mask
|
||||||
|
* at all (mask 0), so sigUsr2 arriving while it is still active is not
|
||||||
|
* blocked by anything -- it genuinely interrupts onUsr1 with its own,
|
||||||
|
* separate trampoline entry, onUsr1 resuming afterwards exactly where the
|
||||||
|
* blocking read below left it. No sleep/timing guess: the three pipes make
|
||||||
|
* "handler installed", "handler entered and blocked", and "safe to unblock
|
||||||
|
* it" all real happens-before rendezvous, the same discipline sigtarget.c/
|
||||||
|
* jobtest.c already established for different races.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static volatile int handlerUsr1Entered = 0;
|
||||||
|
static volatile int handlerUsr2FiredNested = 0;
|
||||||
|
static volatile int handlerUsr1Resumed = 0;
|
||||||
|
|
||||||
|
static long handlerReadyFd = -1;
|
||||||
|
static long goFd = -1;
|
||||||
|
|
||||||
|
static void onUsr2(int signum)
|
||||||
|
{
|
||||||
|
(void)signum;
|
||||||
|
print("nesttarget: usr2-handler-fired-nested\n");
|
||||||
|
handlerUsr2FiredNested = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void onUsr1(int signum)
|
||||||
|
{
|
||||||
|
(void)signum;
|
||||||
|
print("nesttarget: usr1-handler-enter\n");
|
||||||
|
handlerUsr1Entered = 1;
|
||||||
|
|
||||||
|
/* Announces "I am about to block on goFd" from INSIDE the handler --
|
||||||
|
nesttest.c will not send sigUsr2 until it reads this byte, so sigUsr2
|
||||||
|
can never race ahead of onUsr1 actually being active. */
|
||||||
|
if (userWrite((int)handlerReadyFd, "h", 1) != 1) {
|
||||||
|
print("nesttarget: FAIL write handler-ready byte\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userClose((int)handlerReadyFd);
|
||||||
|
|
||||||
|
char goByte = 0;
|
||||||
|
/* Real blocking read, issued BY THE HANDLER ITSELF: the moment it
|
||||||
|
completes, control returns to ring 3 through the exact same
|
||||||
|
int 0x80 path any syscall does, which isr.c's deliverPendingSignals
|
||||||
|
always re-checks -- if sigUsr2 is pending and unblocked by then (it
|
||||||
|
is: onUsr1's own sa_mask is empty), it is delivered as a genuinely
|
||||||
|
NESTED handler right here, before this function's next line ever
|
||||||
|
runs. */
|
||||||
|
if (userRead((int)goFd, &goByte, 1) != 1) {
|
||||||
|
print("nesttarget: FAIL read go byte inside handler\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userClose((int)goFd);
|
||||||
|
|
||||||
|
print("nesttarget: usr1-handler-resumed-after-nested-usr2\n");
|
||||||
|
handlerUsr1Resumed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
print("nesttarget: start, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
|
||||||
|
if (argc != 4) {
|
||||||
|
print("nesttarget: FAIL expected three argv (ready/handlerReady/go handles)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long readyHandle = parseDec(argv[1]);
|
||||||
|
long handlerReadyHandle = parseDec(argv[2]);
|
||||||
|
long goHandle = parseDec(argv[3]);
|
||||||
|
|
||||||
|
/* sigUsr1's sa_mask is 0 (no additional blocking) so sigUsr2 -- also
|
||||||
|
unmasked -- can genuinely nest inside it; sigUsr2 has no mask of its
|
||||||
|
own either, since nothing is meant to interrupt IT here. sigUsr1's
|
||||||
|
own delivery always interrupts main()'s plain userSleep loop below
|
||||||
|
(never a retryable syscall), so its sa_flags is irrelevant either
|
||||||
|
way. sigUsr2, though, genuinely interrupts onUsr1's OWN blocking
|
||||||
|
userRead(goFd) -- this test's whole point is that read transparently
|
||||||
|
resumes and returns the real byte once onUsr2 returns, so sigUsr2
|
||||||
|
is registered WITH sigFlagRestart (signalnum.h); without it, that
|
||||||
|
read would return errInterrupted instead, which
|
||||||
|
user/restarttarget.c -- not this file -- is what actually exercises. */
|
||||||
|
if (userSigAction(sigUsr1, onUsr1, 0, 0) == (long)sigSetFailed ||
|
||||||
|
userSigAction(sigUsr2, onUsr2, 0, sigFlagRestart) == (long)sigSetFailed) {
|
||||||
|
print("nesttarget: FAIL install handlers\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attach the pipes THIS process will use from inside a handler before
|
||||||
|
ever installing/receiving anything that could reach them -- same
|
||||||
|
"attach first" discipline sigtest.c/jobtest.c document at length, so
|
||||||
|
neither slot can be reclaimed out from under a late attach. */
|
||||||
|
handlerReadyFd = userPipeAttach(handlerReadyHandle, 1);
|
||||||
|
goFd = userPipeAttach(goHandle, 0);
|
||||||
|
if (handlerReadyFd < 0 || goFd < 0) {
|
||||||
|
print("nesttarget: FAIL attach handler-ready/go pipes\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long readyFd = userPipeAttach(readyHandle, 1);
|
||||||
|
if (readyFd < 0) {
|
||||||
|
print("nesttarget: FAIL attach ready pipe\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (userWrite((int)readyFd, "r", 1) != 1) {
|
||||||
|
print("nesttarget: FAIL write ready byte\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
userClose((int)readyFd);
|
||||||
|
print("nesttarget: ready, waiting for sigUsr1\n");
|
||||||
|
|
||||||
|
/* Several short sleeps rather than one long one -- this kernel only
|
||||||
|
checks for a pending signal when the target thread actually returns
|
||||||
|
to ring 3 (isr.c's deliverPendingSignals), the same reasoning
|
||||||
|
sigtarget.c's own poll loop documents. Once onUsr1 fires, ALL the
|
||||||
|
real work (including the nested onUsr2) happens synchronously inside
|
||||||
|
it before it ever returns here. */
|
||||||
|
for (int i = 0; i < 80 && !handlerUsr1Resumed; i++)
|
||||||
|
userSleep(20);
|
||||||
|
|
||||||
|
if (!handlerUsr1Entered) {
|
||||||
|
print("nesttarget: FAIL usr1 handler never fired\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!handlerUsr2FiredNested) {
|
||||||
|
print("nesttarget: FAIL usr2 handler never fired nested inside usr1\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!handlerUsr1Resumed) {
|
||||||
|
print("nesttarget: FAIL usr1 handler never resumed after nested usr2\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("nesttarget: nested-delivery-ok\n");
|
||||||
|
|
||||||
|
/* Reached only if BOTH sysSigReturn calls (the nested usr2's and then
|
||||||
|
usr1's own) correctly restored their respective saved contexts --
|
||||||
|
a corrupted pop at either depth would have crashed, hung, or
|
||||||
|
resumed the wrong one of the two saved trapframes instead of
|
||||||
|
returning here from onUsr1's own return. */
|
||||||
|
print("nesttarget: resumed-after-both-handlers-ok\n");
|
||||||
|
return 77;
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include "ulib.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only ever spawned by nicetest.c, with no argv at all -- same "*test /
|
||||||
|
* *target" shape cwdtest.c/cwdtarget.c already has. Sleeps immediately so
|
||||||
|
* nicetest.c gets a real window to renice it from the OUTSIDE while it is
|
||||||
|
* genuinely Sleeping, not just Runnable -- proving sysNice (syscall.c)
|
||||||
|
* reaches a blocked target, not only one that happens to be running right
|
||||||
|
* now. 4096 is an arbitrary but SHARED magic weight, agreed with
|
||||||
|
* nicetest.c's own renice call the same way herestest.c/the real shell
|
||||||
|
* agree on the literal string "hs-ok" -- no header constant ties the two
|
||||||
|
* together, the number is just repeated on both sides.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
print("nicetarget: start, pid=");
|
||||||
|
printDec(userGetpid());
|
||||||
|
print("\n");
|
||||||
|
print("nicetarget: ready, sleeping before priority self-check\n");
|
||||||
|
|
||||||
|
userSleep(200);
|
||||||
|
|
||||||
|
long weight = userGetPriority(0);
|
||||||
|
print("nicetarget: own weight after sleep=");
|
||||||
|
printDec(weight);
|
||||||
|
print("\n");
|
||||||
|
if (weight != 4096) {
|
||||||
|
print("nicetarget: FAIL expected reniced weight 4096\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
print("nicetarget: reniced-while-sleeping-ok\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user