feat: add selfstoptarget and sigchldchild

This commit is contained in:
dualfroz committed 2026-09-09 15:42:48 +02:00
1 parent 67ea1f461e
commit 5585b11968
3 files changed
+122

No files matched your search

+72
View File
@@ -0,0 +1,72 @@
#include "ulib.h"
/*
* Only ever spawned by selfstoptest.c, with argv[1] = a shared pipe handle
* (userPipeCreate'd by selfstoptest.c, decToString'd exactly like every
* other *test / *target pair's own shared-handle argv) this process attaches
* as the WRITER end.
*
* Unlike jobtest.c/jobtarget.c (job control's other live demo), the SIGSTOP
* here targets THIS process's OWN pid -- self-directed job control
* (syscall.c's sysSigSend, the "selfTargeted" branch right after
* sigRaise/threadStop) rather than one process stopping a different one.
* No ready-pipe rendezvous is needed before the self-stop itself: unlike
* jobtest.c's own sigStop (sent from a DIFFERENT process, which could
* otherwise race ahead of jobtarget's own setup), this process is the only
* one that can ever call self-stop, so there is nothing else to order it
* against -- it simply self-stops the moment it has attached the pipe.
*
* selfstoptest.c's own real proof that this process is genuinely Stopped
* (not still running, not merely eventually preempted by the next timer
* tick) is entirely on ITS side (a real SIGCHLD siCodeChldStopped
* notification, then a non-blocking poll proving the pipe below is still
* empty) -- this process only needs to write its own proof byte AFTER
* actually resuming, which can only happen once selfstoptest.c's own
* userSigSend(this pid, sigCont) runs.
*/
int main(int argc, char** argv)
{
print("selfstoptarget: start, pid=");
printDec(userGetpid());
print("\n");
if (argc != 2) {
print("selfstoptarget: FAIL expected one argv (proof pipe handle)\n");
return 1;
}
long handle = parseDec(argv[1]);
long proofFd = userPipeAttach(handle, 1);
if (proofFd < 0) {
print("selfstoptarget: FAIL attach proof pipe as writer\n");
return 1;
}
print("selfstoptarget: self-stopping now\n");
/* Self-targeted SIGSTOP: syscall.c's sysSigSend recognizes rawPid ==
senderPid (this process's own pid) as "selfTargeted", and after
threadStop (called from sigRaise above) flips this thread's own
state to threadStopped, explicitly reschedules instead of just
falling through to its normal return-to-ring3 path -- the exact gap
syscall.c's own comment documents (a self-stopped thread that simply
returned to ring 3 would keep running, unnoticed, until the next
timer tick). Does not return until a real
userSigSend(this pid, sigCont) from selfstoptest.c moves this thread
back to threadRunnable and the scheduler actually picks it again. */
if (userSigSend(userGetpid(), sigStop) != 0) {
print("selfstoptarget: FAIL self-directed sigStop rejected\n");
return 1;
}
print("selfstoptarget: resumed-after-self-stop-ok\n");
if (userWrite((int)proofFd, "p", 1) != 1) {
print("selfstoptarget: FAIL write proof byte\n");
return 1;
}
userClose((int)proofFd);
return 66;
}
+24
View File
@@ -0,0 +1,24 @@
#include "ulib.h"
/*
* Only ever spawned by sigchldtest.c. Does nothing rendezvous-shaped
* (unlike eintrtarget.c/masktarget.c's own targets) because it does not
* need to: sigchldtest.c's own proof (a real sigChld raised by the kernel
* against whoever spawned this process, sched.c's parentId/threadExit)
* does not depend on any particular ordering between this process's own
* death and sigchldtest.c noticing it -- sigRaise sets the pending bit on
* the parent unconditionally, whatever its state is at that instant, and
* sigchldtest.c's own bounded poll loop (the same "check a flag every
* userSleep(20)" idiom masktarget.c/nesttarget.c already use) picks it up
* on its very next natural wake either way. Just runs and exits with a
* distinctive code sigchldtest.c can verify through userWait after the
* fact.
*/
int main(void)
{
print("sigchldchild: start, pid=");
printDec(userGetpid());
print("\n");
print("sigchldchild: exiting now\n");
return 84;
}
+26
View File
@@ -0,0 +1,26 @@
#include "ulib.h"
/*
* Only ever spawned by sigtest.c. Installs no handler at all, so the
* sigTerm sigtest.c sends it must run the OTHER branch of
* signal.c's sigConsumePending: default disposition, which
* isr.c's deliverPendingSignals turns into a real threadExit -- proving a
* signal with no handler actually terminates its target (exit code
* 128+signum, see isr.c), not just that a handler-based one can be caught.
* Reaching the FAIL print below would mean the default-terminate path
* never fired at all.
*/
int main(void)
{
print("sigdefault: start, pid=");
printDec(userGetpid());
print("\n");
print("sigdefault: waiting to be terminated by default disposition\n");
for (int i = 0; i < 40; i++)
userSleep(20);
print("sigdefault: FAIL still alive after default-disposition signal\n");
return 1;
}