Files
2026-08-13 12:25:13 -04:00

80 lines
2.2 KiB
C

/* A child that stays alive after fork (like `cmd &`), works in the parent
* while it does, exchanges messages over pipes, and reaps it at the end.
*
* Every line is ordered by an event -- a pipe read, a write, an exit status --
* never by which process happens to be scheduled.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void put(const char *s) {
/* Unbuffered, because the interleaving between two processes is the point
* and stdio would reorder it into whichever flushed last. */
write(1, s, strlen(s));
}
int main(void) {
int down[2], up[2];
if (pipe(down) != 0 || pipe(up) != 0) {
put("pipe failed\n");
return 1;
}
pid_t kid = fork();
if (kid < 0) {
put("fork failed\n");
return 1;
}
if (kid == 0) {
close(down[1]);
close(up[0]);
/* Nothing is printed before this read: the parent must run to send
* anything, so reaching the end of it is the whole test. A line before
* it would be ordered by scheduling alone. */
char buf[32];
ssize_t n = read(down[0], buf, sizeof buf - 1);
if (n <= 0) {
put("child: the parent never wrote\n");
_exit(2);
}
buf[n] = 0;
put("child: got ");
put(buf);
put("\n");
write(up[1], "ok", 2);
_exit(7);
}
close(down[0]);
close(up[1]);
/* The parent keeps working while the child is alive. */
put("parent: still running\n");
long acc = 0;
for (long i = 0; i < 200000; i++) {
acc += i % 7;
}
if (acc == 0) {
put("parent: impossible\n");
}
put("parent: sending work\n");
write(down[1], "hello", 5);
char reply[8];
ssize_t n = read(up[0], reply, sizeof reply - 1);
reply[n > 0 ? n : 0] = 0;
put("parent: child replied ");
put(reply);
put("\n");
int status = 0;
if (waitpid(kid, &status, 0) != kid) {
put("parent: waitpid did not find the child\n");
return 1;
}
printf("parent: child exited %d\n", WIFEXITED(status) ? WEXITSTATUS(status) : -1);
return 0;
}