xref: /openbsd-src/regress/sys/kern/pipe/test-run-down.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: test-run-down.c,v 1.2 2019/11/14 21:17:00 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
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
73 test_run_down_write_small()
74 {
75 
76 	return test_run_down(1);
77 }
78 
79 static void
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
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
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 {
137 	int error;
138 
139 	error = pthread_mutex_unlock(&ctx->c_mtx);
140 	if (error)
141 		errc(1, error, "pthread_mutex_unlock");
142 }
143 
144 static int
145 test_run_down(size_t bufsiz)
146 {
147 	struct context ctx;
148 	pthread_t th;
149 	int error;
150 
151 	ctx_setup(&ctx, bufsiz);
152 
153 	error = pthread_create(&th, NULL, write_thread, &ctx);
154 	if (error)
155 		errc(1, error, "pthread_create");
156 
157 	ctx_lock(&ctx);
158 	for (;;) {
159 		if (ctx.c_nsyscalls > 0)
160 			break;
161 
162 		error = pthread_cond_wait(&ctx.c_cv, &ctx.c_mtx);
163 		if (error)
164 			errc(1, error, "pthread_cond_wait");
165 	}
166 	ctx_unlock(&ctx);
167 
168 	/* Signal shutdown to the write thread. */
169 	close(ctx.c_pipe[0]);
170 	ctx.c_pipe[0] = -1;
171 
172 	error = pthread_join(th, NULL);
173 	if (error)
174 		errc(1, error, "pthread_join");
175 
176 	if (!gotsigpipe)
177 		errx(1, "no SIGPIPE");
178 
179 	ctx_teardown(&ctx);
180 
181 	return 0;
182 }
183 
184 static void *
185 write_thread(void *arg)
186 {
187 	struct context *ctx = arg;
188 	int error;
189 
190 	for (;;) {
191 		ssize_t n;
192 
193 		n = write(ctx->c_pipe[1], ctx->c_buf, ctx->c_bufsiz);
194 		if (n == -1) {
195 			if (errno == EPIPE)
196 				break;
197 			err(1, "write");
198 		}
199 
200 		ctx_lock(ctx);
201 		ctx->c_nsyscalls++;
202 		ctx_unlock(ctx);
203 
204 		error = pthread_cond_signal(&ctx->c_cv);
205 		if (error)
206 			errc(1, error, "pthread_cond_signal");
207 	}
208 
209 	return NULL;
210 }
211