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 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 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 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 * 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