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