#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "pipe.h"
#define NCHILD 4
struct context {
const char *c_ident;
int c_id;
int c_read;
int c_pipe[2];
int c_cv[2];
char *c_buf;
size_t c_bufsiz;
};
static int test_thundering_herd(int, int);
static pid_t block_proc(const struct context *);
int
test_thundering_herd_read_signal(void)
{
return test_thundering_herd(1, 1);
}
int
test_thundering_herd_read_wakeup(void)
{
return test_thundering_herd(1, 0);
}
int
test_thundering_herd_write_signal(void)
{
return test_thundering_herd(0, 1);
}
int
test_thundering_herd_write_wakeup(void)
{
return test_thundering_herd(0, 0);
}
static int
test_thundering_herd(int doread, int dosignal)
{
pid_t pids[NCHILD];
struct context ctx;
ssize_t n;
int i;
unsigned char c = 'c';
ctx.c_ident = doread ? "read" : "write";
ctx.c_read = doread;
if (pipe(ctx.c_pipe) == -1)
err(1, "pipe");
if (pipe(ctx.c_cv) == -1)
err(1, "pipe");
ctx.c_bufsiz = ctx.c_read ? 1 : BIG_PIPE_SIZE;
ctx.c_buf = malloc(ctx.c_bufsiz);
if (ctx.c_buf == NULL)
err(1, NULL);
for (i = 0; i < NCHILD; i++) {
ctx.c_id = i + 1;
pids[i] = block_proc(&ctx);
}
if (ctx.c_read)
n = write(ctx.c_pipe[1], &c, 1);
else
n = read(ctx.c_pipe[0], &c, 1);
if (n == -1)
err(1, "%s", ctx.c_ident);
if (n != 1)
errx(1, "%s: %ld != 1", ctx.c_ident, n);
(void)read(ctx.c_cv[0], &c, 1);
if (verbose)
fprintf(stderr, "[p] got signal from child\n");
if (dosignal) {
for (i = 0; i < NCHILD; i++) {
if (verbose)
fprintf(stderr, "[p] kill %d\n", i + 1);
if (kill(pids[i], SIGUSR1) == -1)
err(1, "kill");
}
} else {
if (ctx.c_read)
close(ctx.c_pipe[1]);
else
close(ctx.c_pipe[0]);
}
for (i = 0; i < NCHILD; i++) {
int ex;
if (verbose)
fprintf(stderr, "[p] wait %d\n", i + 1);
ex = xwaitpid(pids[i]);
if (ex == 0 || ex == SIGUSR1)
continue;
errx(1, "waitpid: %d != 0", ex);
}
return 0;
}
static pid_t
block_proc(const struct context *ctx)
{
pid_t pid;
pid = fork();
if (pid == -1)
err(1, "fork");
if (pid == 0) {
int rp = ctx->c_pipe[0];
int wp = ctx->c_pipe[1];
if (ctx->c_read)
close(wp);
else
close(rp);
for (;;) {
ssize_t n;
unsigned char c = 'c';
if (ctx->c_read)
n = read(rp, ctx->c_buf, ctx->c_bufsiz);
else
n = write(wp, ctx->c_buf, ctx->c_bufsiz);
if (verbose)
fprintf(stderr, "[%d] %s = %ld\n",
ctx->c_id, ctx->c_ident, n);
if (n == -1) {
if (errno == EPIPE)
break;
err(1, "[%d] %s", ctx->c_id, ctx->c_ident);
}
if (n == 0)
break;
(void)write(ctx->c_cv[1], &c, 1);
}
_exit(0);
}
return pid;
}