Last active
May 25, 2025 12:48
-
-
Save epipping/5c00e0fd4e60a9c34174060551425511 to your computer and use it in GitHub Desktop.
experiment with waitid and waitpid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#define _POSIX_C_SOURCE 199309L // POSIX.1b-1993 | |
//#define _POSIX_C_SOURCE 199506L // POSIX.1c-1996 | |
//#define _POSIX_C_SOURCE 200112L // POSIX.1-2001 | |
#define _POSIX_C_SOURCE 200809L // POSIX.1-2008 | |
#include <assert.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <sys/time.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
#define print_status print_status_waitid_absolute | |
//#define print_status print_status_waitid | |
//#define print_status print_status_waitpid | |
void print_status_waitid_absolute(int pid) { | |
siginfo_t info; | |
// _POSIX_C_SOURCE >= 200809L will do | |
waitid(P_PID, pid, &info, | |
WEXITED | WSTOPPED | WCONTINUED | WNOHANG | WNOWAIT); | |
switch (info.si_code) { | |
case CLD_EXITED: | |
printf(">> Child has EXITED (now or sometime in the past) with status %d\n", | |
info.si_status); | |
return; | |
case CLD_KILLED: | |
case CLD_DUMPED: | |
// not checking if a core was dumped | |
printf( | |
">> Child has been KILLED (now or sometime in the past) by signal %d\n", | |
info.si_status); | |
return; | |
case CLD_STOPPED: | |
printf(">> Child is STOPPED; most recent signal was: %d\n", info.si_status); | |
return; | |
case CLD_CONTINUED: | |
// NOTE: On Linux (documented), this is SIGCONT, the only signal | |
// that can cause a child to continue. On macOS (undocumented), | |
// this appears to be the signal that caused the child to stop. | |
printf(">> Child CONTINUES (somehow related to the signal %d)\n", | |
info.si_status); | |
return; | |
default: | |
printf(">> Child is RUNNING (and has never slept).\n"); | |
return; | |
} | |
} | |
void print_status_waitid(int pid) { | |
siginfo_t info; | |
// _POSIX_C_SOURCE >= 200809L will do | |
waitid(P_PID, pid, &info, WEXITED | WSTOPPED | WCONTINUED | WNOHANG); | |
switch (info.si_code) { | |
case CLD_EXITED: | |
printf(">> Child has changed state to EXITED with status %d\n", | |
info.si_status); | |
return; | |
case CLD_KILLED: | |
case CLD_DUMPED: | |
// not checking if a core was dumped | |
printf(">> Child has changed state to KILLED by signal %d\n", | |
info.si_status); | |
return; | |
case CLD_STOPPED: | |
printf(">> Child has changed state to STOPPED by signal %d\n", | |
info.si_status); | |
return; | |
case CLD_CONTINUED: | |
printf(">> Child has changed state to CONTINUED by signal %d\n", | |
info.si_status); | |
return; | |
default: | |
printf(">> No changes.\n"); | |
return; | |
} | |
} | |
void print_status_waitpid(int pid) { | |
int status; | |
int ret = waitpid(pid, &status, WNOHANG | WUNTRACED); | |
if (ret == -1) { | |
printf(">> No process. Must have terminated in the past.\n"); | |
return; | |
} | |
if (ret == 0) { | |
printf(">> No changes recorded\n"); | |
return; | |
} | |
assert(ret == pid); | |
if (WIFEXITED(status)) { | |
printf(">> Child has changed state to SIGNALED by signal %d\n", | |
WEXITSTATUS(status)); | |
return; | |
} | |
if (WIFCONTINUED(status)) { | |
printf(">> Child has changed state to CONTINUED\n"); | |
return; | |
} | |
if (WIFSIGNALED(status)) { | |
// not checking if a core was dumped via WCOREDUMP() | |
printf(">> Child has changed state to SIGNALED by signal %d\n", | |
WTERMSIG(status)); | |
return; | |
} | |
if (WIFSTOPPED(status)) { | |
printf(">> Child has changed state to STOPPED by signal %d\n", | |
WSTOPSIG(status)); | |
return; | |
} | |
printf("Warning: Should never get here!\n"); | |
} | |
void print_time_since(struct timeval *initial_time) { | |
struct timeval final_time; | |
gettimeofday(&final_time, NULL); | |
printf("Time elapsed: %ld micro seconds\n", | |
(final_time.tv_sec - initial_time->tv_sec) * 1000000 + | |
final_time.tv_usec - initial_time->tv_usec); | |
} | |
int main() { | |
int child_pid = fork(); | |
if (0 == child_pid) { | |
sleep(2); | |
} else { | |
struct timeval initial_time; | |
gettimeofday(&initial_time, NULL); | |
sleep(1); | |
print_status(child_pid); | |
kill(child_pid, SIGSTOP); | |
puts("SIGSTOP sent"); | |
print_status(child_pid); | |
sleep(1); | |
puts("slept for 1s"); | |
print_status(child_pid); | |
sleep(1); | |
puts("slept for 1s"); | |
print_status(child_pid); | |
kill(child_pid, SIGCONT); | |
puts("SIGCONT sent"); | |
print_status(child_pid); | |
print_status(child_pid); | |
sleep(2); | |
puts("waited for 2 seconds (child should be done now)"); | |
print_status(child_pid); | |
print_status(child_pid); | |
print_time_since(&initial_time); | |
} | |
wait(NULL); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It appears that on Darwin,
WCONTINUED
only works if the child process isSIGCONT
'd by the parent, not by other unrelated processes.kill -CONT $child_pid
from another terminal does not makewaitid
orwaitpid
return.