Suppose I have a process which spawns exactly one child process. Now when the parent process exits for whatever reason (normally or abnormally, by kill, ^C, assert failure o
Does the child process have a pipe to/from the parent process? If so, you'd receive a SIGPIPE if writing, or get EOF when reading - these conditions could be detected.
If parent dies, PPID of orphans change to 1 - you only need to check your own PPID. In a way, this is polling, mentioned above. here is shell piece for that:
check_parent () {
parent=`ps -f|awk '$2=='$PID'{print $3 }'`
echo "parent:$parent"
let parent=$parent+0
if [[ $parent -eq 1 ]]; then
echo "parent is dead, exiting"
exit;
fi
}
PID=$$
cnt=0
while [[ 1 = 1 ]]; do
check_parent
... something
done
I managed to do a portable, non-polling solution with 3 processes by abusing terminal control and sessions. This is mental masturbation, but works.
The trick is:
That way:
Shortcomings:
For completeness sake. On macOS you can use kqueue:
void noteProcDeath(
CFFileDescriptorRef fdref,
CFOptionFlags callBackTypes,
void* info)
{
// LOG_DEBUG(@"noteProcDeath... ");
struct kevent kev;
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
kevent(fd, NULL, 0, &kev, 1, NULL);
// take action on death of process here
unsigned int dead_pid = (unsigned int)kev.ident;
CFFileDescriptorInvalidate(fdref);
CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example
int our_pid = getpid();
// when our parent dies we die as well..
LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid);
exit(EXIT_SUCCESS);
}
void suicide_if_we_become_a_zombie(int parent_pid) {
// int parent_pid = getppid();
// int our_pid = getpid();
// LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid);
int fd = kqueue();
struct kevent kev;
EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL);
kevent(fd, &kev, 1, NULL, 0, NULL);
CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL);
CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack);
CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
I'm trying to solve the same problem, and since my program must run on OS X, the Linux-only solution didn't work for me.
I came to the same conclusion as the other people on this page -- there isn't a POSIX-compatible way of notifying a child when a parent dies. So I kludged up the next-best thing -- having the child poll.
When a parent process dies (for any reason) the child's parent process becomes process 1. If the child simply polls periodically, it can check if its parent is 1. If it is, the child should exit.
This isn't great, but it works, and it's easier than the TCP socket/lockfile polling solutions suggested elsewhere on this page.
Even though 7 years have passed I've just run into this issue as I'm running SpringBoot application that needs to start webpack-dev-server during development and needs to kill it when the backend process stops.
I try to use Runtime.getRuntime().addShutdownHook
but it worked on Windows 10 but not on Windows 7.
I've change it to use a dedicated thread that waits for the process to quit or for InterruptedException
which seems to work correctly on both Windows versions.
private void startWebpackDevServer() {
String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
logger.info("webpack dev-server " + cmd);
Thread thread = new Thread(() -> {
ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
pb.redirectError(ProcessBuilder.Redirect.INHERIT);
pb.directory(new File("."));
Process process = null;
try {
// Start the node process
process = pb.start();
// Wait for the node process to quit (blocking)
process.waitFor();
// Ensure the node process is killed
process.destroyForcibly();
System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
} catch (InterruptedException | IOException e) {
// Ensure the node process is killed.
// InterruptedException is thrown when the main process exit.
logger.info("killing webpack dev-server", e);
if (process != null) {
process.destroyForcibly();
}
}
});
thread.start();
}