Skip to content

Instantly share code, notes, and snippets.

@frehberg
Last active December 28, 2019 21:50
Show Gist options
  • Save frehberg/58cac82a332febd6aff0e188c825b699 to your computer and use it in GitHub Desktop.
Save frehberg/58cac82a332febd6aff0e188c825b699 to your computer and use it in GitHub Desktop.
C death pipe
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
/**
* This application is creating a child-process and waiting until the child-process
* did terminate.
*
* Between parent process and child-process a death-pipe is established. When the child-process
* terminates, an event appears on the read-end of the death-pipe. The parent-process
* will poll for the POLLIN-event, waiting for the termination of the child-process.
*
* The parent process can be executed in two modes:
* 1) The NOT-QT_MODE is keeping the write-end in child-process and keeping the read-end
* in parent-process.
* > a.out not-qt-mode
*
* 2) The QT_MODE is closing all file descriptors in child-process and keeping both file-descriptors in
* parent-process.
* > a.out qt-mode
*
* Finally the parent process will verify the status of the child-process. In case the child-process
* did not terminate as expected, an error message will be printed.
*/
#define NOT_QT_MODE "not-qt-mode"
#define QT_MODE "qt-mode"
// seceonds
#define SLEEP_SECONDS 3
int main(int argc, char *argv[], char *envp[]) {
char *mode = argc > 1 ? argv[1] : NOT_QT_MODE;
int qtmode = strcmp(QT_MODE, mode) == 0;
int isChild = argc > 2;
int pid;
int death_pipe[2];
if (isChild) {
printf("[child] sub-process via execve\n");
printf("[child] ..sleeping\n");
sleep(SLEEP_SECONDS);
printf("[child] ..terminating\n");
exit(0);
}
printf("[parent] forking child using '%s'\n", mode);
if (pipe(death_pipe) < 0) {
printf("[parent] ..failed to create pipe");
exit(1);
}
if ((pid = fork()) < 0) {
printf("[parent] ..failed to fork");
exit(1);
}
if (0 == pid) {
// child
printf("[child] setting up child-context\n");
if (qtmode) {
// working the way QtProcess is working
// closing both ends of death_pipe in child-process
close(death_pipe[0]);
close(death_pipe[1]);
} else {
// working the other way
// in child-process, prevent the writing end will be inherited to further child-processes,
// and closing just the reading end of the death_pipe
close(death_pipe[0]);
}
// in child-process context, execute a new binary
char * const command[] = {argv[0], mode, "child", NULL};
if (execve(argv[0], command, envp) < 0) {
printf("[child] ..failed to execve\n");
printf("[child] ..terminating with error\n");
exit(1);
}
} else {
// parent
int milli_timeout = 500;
int pollcnt = 0;
struct pollfd pollset[1];
pollset[0].fd = death_pipe[0];
pollset[0].events = POLLIN;
pollset[0].revents = 0;
int cnt_down = 2 * SLEEP_SECONDS * 1000 / milli_timeout;
if (qtmode) {
// keeping both ends of death_üpipe in parent-process
// don't close anything
} else {
// closing the writng end of the pipe
close(death_pipe[1]);
}
while (--cnt_down > 0 && (pollcnt = poll(pollset, 1, milli_timeout)) == 0) {
printf("[parent] ..awaiting child-proc termination %d\n", cnt_down);
}
if (cnt_down <= 0) {
printf("[parent] FAILURE, ime exceeded, death_pipe did not trigger event\n");
} else {
printf("[parent] death_pipe triggered event: pollrv=%d\n", pollcnt);
}
// death_pipe signal occured, now verify the child did terminate
if (waitpid(pid, NULL, WNOHANG)==0) {
printf("[parent] FAILURE, faulty death_pipe event, the child-process did not terminate yet\n");
printf("[parent] ..waiting for child-process termination\n");
wait(NULL);
}
}
printf("[parent] terminating\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment