xref: /openbsd-src/regress/sys/kern/pipe/test-run-down.c (revision 065bbfdcdf552524ec75f6af3b0eb1650b554db7)
1 /*	$OpenBSD: test-run-down.c,v 1.3 2021/10/22 05:03:57 anton Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <err.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <pthread.h>
23 #include <unistd.h>
24 
25 #include "pipe.h"
26 
27 struct context {
28 	int c_pipe[2];
29 	char *c_buf;
30 	size_t c_bufsiz;
31 	unsigned int c_nsyscalls;
32 
33 	pthread_cond_t c_cv;
34 	pthread_mutex_t c_mtx;
35 };
36 
37 static void ctx_setup(struct context *, size_t);
38 static void ctx_teardown(struct context *);
39 static void ctx_lock(struct context *);
40 static void ctx_unlock(struct context *);
41 
42 static int test_run_down(size_t);
43 
44 static void *write_thread(void *);
45 
46 /*
47  * Verify delivery of SIGPIPE while trying to write on a pipe where the read end
48  * is gone.
49  *
50  * The writer thread first of writes BIG_PIPE_SIZE number of bytes and then
51  * tries to perform the same operation again. The ambition is to cause the
52  * writer thread to go to sleep since the pipe capacity is exhausted. The main
53  * thread closes the read end at this point causing the writer thread to wake up
54  * and punt.
55  */
56 int
test_run_down_write_big()57 test_run_down_write_big()
58 {
59 
60 	return test_run_down(BIG_PIPE_SIZE);
61 }
62 
63 /*
64  * Verify delivery of SIGPIPE while trying to write on a pipe where the read end
65  * is gone.
66  *
67  * The writer thread writes a single byte continously while the main thread ends
68  * up closing the read end after at least a single byte has been written. The
69  * ambition is not to make the writer thread go to sleep but rather punt early
70  * while writing since the read end is gone.
71  */
72 int
test_run_down_write_small()73 test_run_down_write_small()
74 {
75 
76 	return test_run_down(1);
77 }
78 
79 static void
ctx_setup(struct context * ctx,size_t bufsiz)80 ctx_setup(struct context *ctx, size_t bufsiz)
81 {
82 	int error;
83 
84 	if (pipe(ctx->c_pipe) == -1)
85 		err(1, "pipe");
86 
87 	ctx->c_buf = malloc(bufsiz);
88 	if (ctx->c_buf == NULL)
89 		err(1, NULL);
90 	ctx->c_bufsiz = bufsiz;
91 	ctx->c_nsyscalls = 0;
92 
93 	error = pthread_cond_init(&ctx->c_cv, NULL);
94 	if (error)
95 		errc(1, error, "pthread_cond_init");
96 
97 	error = pthread_mutex_init(&ctx->c_mtx, NULL);
98 	if (error)
99 		errc(1, error, "pthread_mutex_init");
100 }
101 
102 static void
ctx_teardown(struct context * ctx)103 ctx_teardown(struct context *ctx)
104 {
105 	int error;
106 
107 	if (ctx->c_pipe[0] != -1)
108 		close(ctx->c_pipe[0]);
109 	ctx->c_pipe[0] = -1;
110 	if (ctx->c_pipe[1] != -1)
111 		close(ctx->c_pipe[1]);
112 	ctx->c_pipe[1] = -1;
113 
114 	free(ctx->c_buf);
115 
116 	error = pthread_cond_destroy(&ctx->c_cv);
117 	if (error)
118 		errc(1, error, "pthread_cond_destroy");
119 
120 	error = pthread_mutex_destroy(&ctx->c_mtx);
121 	if (error)
122 		errc(1, error, "pthread_mutex_destroy");
123 }
124 
125 static void
ctx_lock(struct context * ctx)126 ctx_lock(struct context *ctx)
127 {
128 	int error;
129 
130 	error = pthread_mutex_lock(&ctx->c_mtx);
131 	if (error)
132 		errc(1, error, "pthread_mutex_lock");
133 }
134 
135 static void
ctx_unlock(struct context * ctx)136 ctx_unlock(struct context *ctx)
137 {
138 	int error;
139 
140 	error = pthread_mutex_unlock(&ctx->c_mtx);
141 	if (error)
142 		errc(1, error, "pthread_mutex_unlock");
143 }
144 
145 static int
test_run_down(size_t bufsiz)146 test_run_down(size_t bufsiz)
147 {
148 	struct context ctx;
149 	pthread_t th;
150 	int error;
151 
152 	ctx_setup(&ctx, bufsiz);
153 
154 	error = pthread_create(&th, NULL, write_thread, &ctx);
155 	if (error)
156 		errc(1, error, "pthread_create");
157 
158 	ctx_lock(&ctx);
159 	for (;;) {
160 		if (ctx.c_nsyscalls > 0)
161 			break;
162 
163 		error = pthread_cond_wait(&ctx.c_cv, &ctx.c_mtx);
164 		if (error)
165 			errc(1, error, "pthread_cond_wait");
166 	}
167 	ctx_unlock(&ctx);
168 
169 	/* Signal shutdown to the write thread. */
170 	close(ctx.c_pipe[0]);
171 	ctx.c_pipe[0] = -1;
172 
173 	error = pthread_join(th, NULL);
174 	if (error)
175 		errc(1, error, "pthread_join");
176 
177 	if (!gotsigpipe)
178 		errx(1, "no SIGPIPE");
179 
180 	ctx_teardown(&ctx);
181 
182 	return 0;
183 }
184 
185 static void *
write_thread(void * arg)186 write_thread(void *arg)
187 {
188 	struct context *ctx = arg;
189 	int error;
190 
191 	for (;;) {
192 		ssize_t n;
193 
194 		n = write(ctx->c_pipe[1], ctx->c_buf, ctx->c_bufsiz);
195 		if (n == -1) {
196 			if (errno == EPIPE)
197 				break;
198 			err(1, "write");
199 		}
200 
201 		ctx_lock(ctx);
202 		ctx->c_nsyscalls++;
203 		ctx_unlock(ctx);
204 
205 		error = pthread_cond_signal(&ctx->c_cv);
206 		if (error)
207 			errc(1, error, "pthread_cond_signal");
208 	}
209 
210 	return NULL;
211 }
212