1 /* $OpenBSD: test-kqueue.c,v 1.3 2021/05/08 06:53:19 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 <sys/types.h> 20 #include <sys/event.h> 21 #include <sys/time.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <pthread.h> 27 #include <stdlib.h> 28 #include <unistd.h> 29 30 #include "pipe.h" 31 32 enum kqueue_mode { 33 KQUEUE_READ, 34 KQUEUE_READ_EOF, 35 KQUEUE_WRITE, 36 KQUEUE_WRITE_EOF, 37 }; 38 39 struct context { 40 enum kqueue_mode c_mode; 41 int c_alive; 42 43 int c_pipe[2]; 44 int c_kq; 45 46 char *c_buf; 47 size_t c_bufsiz; 48 49 pthread_t c_th; 50 pthread_mutex_t c_mtx; 51 }; 52 53 static void ctx_setup(struct context *, enum kqueue_mode, int); 54 static void ctx_teardown(struct context *); 55 static int ctx_thread_alive(struct context *); 56 static void ctx_thread_start(struct context *); 57 static void ctx_lock(struct context *); 58 static void ctx_unlock(struct context *); 59 60 static void *kqueue_thread(void *); 61 62 /* 63 * Verify kqueue read event. 64 */ 65 int 66 test_kqueue_read(void) 67 { 68 struct context ctx; 69 70 ctx_setup(&ctx, KQUEUE_READ, O_NONBLOCK); 71 ctx_thread_start(&ctx); 72 73 while (ctx_thread_alive(&ctx)) { 74 ssize_t n; 75 76 n = write(ctx.c_pipe[1], &ctx.c_buf[0], 1); 77 if (n == -1) { 78 if (errno == EAGAIN) 79 continue; 80 err(1, "write"); 81 } 82 if (n != 1) 83 errx(1, "write: %ld != 1", n); 84 } 85 86 ctx_teardown(&ctx); 87 88 return 0; 89 } 90 91 /* 92 * Verify kqueue read EOF event. 93 */ 94 int 95 test_kqueue_read_eof(void) 96 { 97 struct context ctx; 98 99 ctx_setup(&ctx, KQUEUE_READ_EOF, 0); 100 ctx_thread_start(&ctx); 101 102 while (ctx_thread_alive(&ctx)) { 103 if (ctx.c_pipe[1] == -1) 104 continue; 105 106 close(ctx.c_pipe[1]); 107 ctx.c_pipe[1] = -1; 108 } 109 110 ctx_teardown(&ctx); 111 112 return 0; 113 } 114 115 /* 116 * Verify kqueue write event. 117 */ 118 int 119 test_kqueue_write(void) 120 { 121 struct context ctx; 122 ssize_t n; 123 124 ctx_setup(&ctx, KQUEUE_WRITE, 0); 125 126 n = write(ctx.c_pipe[1], ctx.c_buf, ctx.c_bufsiz); 127 if (n == -1) 128 err(1, "write"); 129 if ((size_t)n != ctx.c_bufsiz) 130 errx(1, "write: %ld != %zu", n, ctx.c_bufsiz); 131 132 ctx_thread_start(&ctx); 133 134 while (ctx_thread_alive(&ctx)) { 135 unsigned char c; 136 137 n = read(ctx.c_pipe[0], &c, 1); 138 if (n == -1) 139 err(1, "read"); 140 if (n != 1) 141 errx(1, "read: %ld != 1", n); 142 } 143 144 ctx_teardown(&ctx); 145 146 return 0; 147 } 148 149 /* 150 * XXX Verify kqueue write event. 151 */ 152 int 153 test_kqueue_write_eof(void) 154 { 155 156 return 0; 157 } 158 159 static void 160 ctx_setup(struct context *ctx, enum kqueue_mode mode, int flags) 161 { 162 int error; 163 164 ctx->c_mode = mode; 165 ctx->c_alive = 1; 166 167 if (flags) { 168 if (pipe2(ctx->c_pipe, flags) == -1) 169 err(1, "pipe"); 170 } else { 171 if (pipe(ctx->c_pipe) == -1) 172 err(1, "pipe"); 173 } 174 175 ctx->c_kq = kqueue(); 176 if (ctx->c_kq == -1) 177 err(1, "kqueue"); 178 179 ctx->c_bufsiz = PIPE_SIZE; 180 ctx->c_buf = malloc(ctx->c_bufsiz); 181 if (ctx->c_buf == NULL) 182 err(1, NULL); 183 184 error = pthread_mutex_init(&ctx->c_mtx, NULL); 185 if (error) 186 errc(1, error, "pthread_mutex_init"); 187 } 188 189 static void 190 ctx_teardown(struct context *ctx) 191 { 192 int error; 193 194 error = pthread_join(ctx->c_th, NULL); 195 if (error) 196 errc(1, error, "pthread_join"); 197 198 error = pthread_mutex_destroy(&ctx->c_mtx); 199 if (error) 200 errc(1, error, "pthread_mutex_destroy"); 201 202 free(ctx->c_buf); 203 204 close(ctx->c_pipe[0]); 205 close(ctx->c_pipe[1]); 206 close(ctx->c_kq); 207 208 } 209 210 static int 211 ctx_thread_alive(struct context *ctx) 212 { 213 int alive; 214 215 ctx_lock(ctx); 216 alive = ctx->c_alive; 217 ctx_unlock(ctx); 218 return alive; 219 } 220 221 static void 222 ctx_thread_start(struct context *ctx) 223 { 224 int error; 225 226 error = pthread_create(&ctx->c_th, NULL, kqueue_thread, ctx); 227 if (error) 228 errc(1, error, "pthread_create"); 229 } 230 231 static void 232 ctx_lock(struct context *ctx) 233 { 234 int error; 235 236 error = pthread_mutex_lock(&ctx->c_mtx); 237 if (error) 238 errc(1, error, "pthread_mutex_lock"); 239 } 240 241 static void ctx_unlock(struct context *ctx) 242 { 243 int error; 244 245 error = pthread_mutex_unlock(&ctx->c_mtx); 246 if (error) 247 errc(1, error, "pthread_mutex_unlock"); 248 } 249 250 static void * 251 kqueue_thread(void *arg) 252 { 253 struct context *ctx = arg; 254 struct kevent kev; 255 int fd, filter, nevents; 256 257 switch (ctx->c_mode) { 258 case KQUEUE_READ: 259 case KQUEUE_READ_EOF: 260 fd = ctx->c_pipe[0]; 261 filter = EVFILT_READ; 262 break; 263 case KQUEUE_WRITE: 264 case KQUEUE_WRITE_EOF: 265 fd = ctx->c_pipe[1]; 266 filter = EVFILT_WRITE; 267 break; 268 } 269 270 EV_SET(&kev, fd, filter, EV_ADD, 0, 0, NULL); 271 nevents = kevent(ctx->c_kq, &kev, 1, NULL, 0, NULL); 272 if (nevents == -1) 273 err(1, "kevent"); 274 nevents = kevent(ctx->c_kq, NULL, 0, &kev, 1, NULL); 275 if (nevents == -1) 276 err(1, "kevent"); 277 if (nevents != 1) 278 errx(1, "kevent: %d != 1", nevents); 279 280 if ((int)kev.ident != fd) 281 errx(1, "kevent: ident"); 282 if (kev.filter != filter) 283 errx(1, "kevent: filter"); 284 285 switch (ctx->c_mode) { 286 case KQUEUE_READ_EOF: 287 case KQUEUE_WRITE_EOF: 288 if ((kev.flags & EV_EOF) == 0) 289 errx(1, "kevent: eof"); 290 break; 291 default: 292 break; 293 } 294 295 ctx_lock(ctx); 296 ctx->c_alive = 0; 297 ctx_unlock(ctx); 298 299 return NULL; 300 } 301