1*63f99460Santon /*	$OpenBSD: test-thundering-herd.c,v 1.2 2019/11/14 21:17:00 anton Exp $	*/
279596596Santon 
379596596Santon /*
479596596Santon  * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org>
579596596Santon  *
679596596Santon  * Permission to use, copy, modify, and distribute this software for any
779596596Santon  * purpose with or without fee is hereby granted, provided that the above
879596596Santon  * copyright notice and this permission notice appear in all copies.
979596596Santon  *
1079596596Santon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1179596596Santon  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1279596596Santon  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1379596596Santon  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1479596596Santon  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1579596596Santon  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1679596596Santon  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1779596596Santon  */
1879596596Santon 
1979596596Santon /*
2079596596Santon  * Verify correctness when multiple threads are waiting on I/O in either
2179596596Santon  * pipe_read(9) or pipe_write(9).
2279596596Santon  *
2379596596Santon  * Multiple threads are put in this waiting state. The parent thread then
2479596596Santon  * performs a read/write operation on the pipe causing all sleeping threads to
2579596596Santon  * be woken up; only to end up sleeping in pipelock(9). The parent thread then
2679596596Santon  * either closes the pipe or sends a signal to all threads, which must cause all
2779596596Santon  * threads to abort its pipe operation.
2879596596Santon  */
2979596596Santon 
3079596596Santon #include <err.h>
3179596596Santon #include <errno.h>
3279596596Santon #include <stdio.h>
3379596596Santon #include <stdlib.h>
3479596596Santon #include <unistd.h>
3579596596Santon 
3679596596Santon #include "pipe.h"
3779596596Santon 
3879596596Santon #define NCHILD	4
3979596596Santon 
4079596596Santon struct context {
4179596596Santon 	const char *c_ident;
4279596596Santon 	int c_id;
4379596596Santon 	int c_read;
4479596596Santon 
4579596596Santon 	int c_pipe[2];
4679596596Santon 	int c_cv[2];
4779596596Santon 
4879596596Santon 	char *c_buf;
4979596596Santon 	size_t c_bufsiz;
5079596596Santon };
5179596596Santon 
5279596596Santon static int test_thundering_herd(int, int);
5379596596Santon 
5479596596Santon static pid_t block_proc(const struct context *);
5579596596Santon 
5679596596Santon int
test_thundering_herd_read_signal(void)5779596596Santon test_thundering_herd_read_signal(void)
5879596596Santon {
5979596596Santon 
6079596596Santon 	return test_thundering_herd(1, 1);
6179596596Santon }
6279596596Santon 
6379596596Santon int
test_thundering_herd_read_wakeup(void)6479596596Santon test_thundering_herd_read_wakeup(void)
6579596596Santon {
6679596596Santon 
6779596596Santon 	return test_thundering_herd(1, 0);
6879596596Santon }
6979596596Santon 
7079596596Santon int
test_thundering_herd_write_signal(void)7179596596Santon test_thundering_herd_write_signal(void)
7279596596Santon {
7379596596Santon 
7479596596Santon 	return test_thundering_herd(0, 1);
7579596596Santon }
7679596596Santon 
7779596596Santon int
test_thundering_herd_write_wakeup(void)7879596596Santon test_thundering_herd_write_wakeup(void)
7979596596Santon {
8079596596Santon 
8179596596Santon 	return test_thundering_herd(0, 0);
8279596596Santon }
8379596596Santon 
8479596596Santon static int
test_thundering_herd(int doread,int dosignal)8579596596Santon test_thundering_herd(int doread, int dosignal)
8679596596Santon {
8779596596Santon 	pid_t pids[NCHILD];
8879596596Santon 	struct context ctx;
8979596596Santon 	ssize_t n;
9079596596Santon 	int i;
9179596596Santon 	unsigned char c = 'c';
9279596596Santon 
9379596596Santon 	ctx.c_ident = doread ? "read" : "write";
9479596596Santon 	ctx.c_read = doread;
9579596596Santon 	if (pipe(ctx.c_pipe) == -1)
9679596596Santon 		err(1, "pipe");
9779596596Santon 	if (pipe(ctx.c_cv) == -1)
9879596596Santon 		err(1, "pipe");
9979596596Santon 
100*63f99460Santon 	ctx.c_bufsiz = ctx.c_read ? 1 : BIG_PIPE_SIZE;
10179596596Santon 	ctx.c_buf = malloc(ctx.c_bufsiz);
10279596596Santon 	if (ctx.c_buf == NULL)
10379596596Santon 		err(1, NULL);
10479596596Santon 
10579596596Santon 	for (i = 0; i < NCHILD; i++) {
10679596596Santon 		ctx.c_id = i + 1;
10779596596Santon 		pids[i] = block_proc(&ctx);
10879596596Santon 	}
10979596596Santon 
11079596596Santon 	/*
11179596596Santon 	 * Let one child wakeup and force the other children into sleeping in
11279596596Santon 	 * pipelock(9).
11379596596Santon 	 */
11479596596Santon 	if (ctx.c_read)
11579596596Santon 		n = write(ctx.c_pipe[1], &c, 1);
11679596596Santon 	else
11779596596Santon 		n = read(ctx.c_pipe[0], &c, 1);
11879596596Santon 	if (n == -1)
11979596596Santon 		err(1, "%s", ctx.c_ident);
12079596596Santon 	if (n != 1)
12179596596Santon 		errx(1, "%s: %ld != 1", ctx.c_ident, n);
12279596596Santon 
12379596596Santon 	/* Wait for signal from woken up child. */
12479596596Santon 	(void)read(ctx.c_cv[0], &c, 1);
12579596596Santon 	if (verbose)
12679596596Santon 		fprintf(stderr, "[p] got signal from child\n");
12779596596Santon 
12879596596Santon 	if (dosignal) {
12979596596Santon 		for (i = 0; i < NCHILD; i++) {
13079596596Santon 			if (verbose)
13179596596Santon 				fprintf(stderr, "[p] kill %d\n", i + 1);
13279596596Santon 			if (kill(pids[i], SIGUSR1) == -1)
13379596596Santon 				err(1, "kill");
13479596596Santon 		}
13579596596Santon 	} else {
13679596596Santon 		if (ctx.c_read)
13779596596Santon 			close(ctx.c_pipe[1]);
13879596596Santon 		else
13979596596Santon 			close(ctx.c_pipe[0]);
14079596596Santon 	}
14179596596Santon 
14279596596Santon 	for (i = 0; i < NCHILD; i++) {
14379596596Santon 		int ex;
14479596596Santon 
14579596596Santon 		if (verbose)
14679596596Santon 			fprintf(stderr, "[p] wait %d\n", i + 1);
14779596596Santon 		ex = xwaitpid(pids[i]);
14879596596Santon 		if (ex == 0 || ex == SIGUSR1)
14979596596Santon 			continue;
15079596596Santon 		errx(1, "waitpid: %d != 0", ex);
15179596596Santon 	}
15279596596Santon 
15379596596Santon 	return 0;
15479596596Santon }
15579596596Santon 
15679596596Santon static pid_t
block_proc(const struct context * ctx)15779596596Santon block_proc(const struct context *ctx)
15879596596Santon {
15979596596Santon 	pid_t pid;
16079596596Santon 
16179596596Santon 	pid = fork();
16279596596Santon 	if (pid == -1)
16379596596Santon 		err(1, "fork");
16479596596Santon 	if (pid == 0) {
16579596596Santon 		int rp = ctx->c_pipe[0];
16679596596Santon 		int wp = ctx->c_pipe[1];
16779596596Santon 
16879596596Santon 		if (ctx->c_read)
16979596596Santon 			close(wp);
17079596596Santon 		else
17179596596Santon 			close(rp);
17279596596Santon 
17379596596Santon 		for (;;) {
17479596596Santon 			ssize_t n;
17579596596Santon 			unsigned char c = 'c';
17679596596Santon 
17779596596Santon 			if (ctx->c_read)
17879596596Santon 				n = read(rp, ctx->c_buf, ctx->c_bufsiz);
17979596596Santon 			else
18079596596Santon 				n = write(wp, ctx->c_buf, ctx->c_bufsiz);
18179596596Santon 			if (verbose)
18279596596Santon 				fprintf(stderr, "[%d] %s = %ld\n",
18379596596Santon 				    ctx->c_id, ctx->c_ident, n);
18479596596Santon 			if (n == -1) {
18579596596Santon 				if (errno == EPIPE)
18679596596Santon 					break;
18779596596Santon 				err(1, "[%d] %s", ctx->c_id, ctx->c_ident);
18879596596Santon 			}
18979596596Santon 			if (n == 0)
19079596596Santon 				break;
19179596596Santon 
19279596596Santon 			/* Send signal to parent. */
19379596596Santon 			(void)write(ctx->c_cv[1], &c, 1);
19479596596Santon 		}
19579596596Santon 
19679596596Santon 		_exit(0);
19779596596Santon 	}
19879596596Santon 
19979596596Santon 	return pid;
20079596596Santon }
201