1*d2aeeac5Santon /* $OpenBSD: test-kqueue.c,v 1.6 2023/10/14 13:05:43 anton Exp $ */
2e22febffSanton
3e22febffSanton /*
4e22febffSanton * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org>
5e22febffSanton *
6e22febffSanton * Permission to use, copy, modify, and distribute this software for any
7e22febffSanton * purpose with or without fee is hereby granted, provided that the above
8e22febffSanton * copyright notice and this permission notice appear in all copies.
9e22febffSanton *
10e22febffSanton * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11e22febffSanton * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12e22febffSanton * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13e22febffSanton * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14e22febffSanton * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15e22febffSanton * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16e22febffSanton * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e22febffSanton */
18e22febffSanton
19e22febffSanton #include <sys/types.h>
20e22febffSanton #include <sys/event.h>
21e22febffSanton #include <sys/time.h>
22e22febffSanton
23e22febffSanton #include <err.h>
24fc11865aSanton #include <errno.h>
25fc11865aSanton #include <fcntl.h>
26e22febffSanton #include <pthread.h>
27e22febffSanton #include <stdlib.h>
28e22febffSanton #include <unistd.h>
29e22febffSanton
30e22febffSanton #include "pipe.h"
31e22febffSanton
32e22febffSanton enum kqueue_mode {
33e22febffSanton KQUEUE_READ,
34e22febffSanton KQUEUE_READ_EOF,
35e22febffSanton KQUEUE_WRITE,
36e22febffSanton KQUEUE_WRITE_EOF,
37e22febffSanton };
38e22febffSanton
39e22febffSanton struct context {
40e22febffSanton enum kqueue_mode c_mode;
41e22febffSanton int c_alive;
42e22febffSanton
43e22febffSanton int c_pipe[2];
44e22febffSanton int c_kq;
45e22febffSanton
46e22febffSanton char *c_buf;
47e22febffSanton size_t c_bufsiz;
48e22febffSanton
49e22febffSanton pthread_t c_th;
50e22febffSanton pthread_mutex_t c_mtx;
51e22febffSanton };
52e22febffSanton
53fc11865aSanton static void ctx_setup(struct context *, enum kqueue_mode, int);
54e22febffSanton static void ctx_teardown(struct context *);
55e22febffSanton static int ctx_thread_alive(struct context *);
56e22febffSanton static void ctx_thread_start(struct context *);
57e22febffSanton static void ctx_lock(struct context *);
58e22febffSanton static void ctx_unlock(struct context *);
59e22febffSanton
60e22febffSanton static void *kqueue_thread(void *);
61e22febffSanton
62e22febffSanton /*
63e22febffSanton * Verify kqueue read event.
64e22febffSanton */
65e22febffSanton int
test_kqueue_read(void)66e22febffSanton test_kqueue_read(void)
67e22febffSanton {
68e22febffSanton struct context ctx;
69e22febffSanton
70fc11865aSanton ctx_setup(&ctx, KQUEUE_READ, O_NONBLOCK);
71e22febffSanton ctx_thread_start(&ctx);
72e22febffSanton
73e22febffSanton while (ctx_thread_alive(&ctx)) {
74e22febffSanton ssize_t n;
75e22febffSanton
76fc11865aSanton n = write(ctx.c_pipe[1], &ctx.c_buf[0], 1);
77fc11865aSanton if (n == -1) {
78*d2aeeac5Santon if (errno == EPIPE)
79*d2aeeac5Santon break;
80fc11865aSanton if (errno == EAGAIN)
81fc11865aSanton continue;
82e22febffSanton err(1, "write");
83fc11865aSanton }
84e22febffSanton if (n != 1)
85e22febffSanton errx(1, "write: %ld != 1", n);
86e22febffSanton }
87e22febffSanton
88e22febffSanton ctx_teardown(&ctx);
89e22febffSanton
90e22febffSanton return 0;
91e22febffSanton }
92e22febffSanton
93e22febffSanton /*
94e22febffSanton * Verify kqueue read EOF event.
95e22febffSanton */
96e22febffSanton int
test_kqueue_read_eof(void)97e22febffSanton test_kqueue_read_eof(void)
98e22febffSanton {
99e22febffSanton struct context ctx;
100e22febffSanton
101fc11865aSanton ctx_setup(&ctx, KQUEUE_READ_EOF, 0);
102e22febffSanton ctx_thread_start(&ctx);
103e22febffSanton
104e22febffSanton while (ctx_thread_alive(&ctx)) {
105e22febffSanton if (ctx.c_pipe[1] == -1)
106e22febffSanton continue;
107e22febffSanton
108e22febffSanton close(ctx.c_pipe[1]);
109e22febffSanton ctx.c_pipe[1] = -1;
110e22febffSanton }
111e22febffSanton
112e22febffSanton ctx_teardown(&ctx);
113e22febffSanton
114e22febffSanton return 0;
115e22febffSanton }
116e22febffSanton
117e22febffSanton /*
118e22febffSanton * Verify kqueue write event.
119e22febffSanton */
120e22febffSanton int
test_kqueue_write(void)121e22febffSanton test_kqueue_write(void)
122e22febffSanton {
123e22febffSanton struct context ctx;
124e22febffSanton ssize_t n;
125e22febffSanton
126fc11865aSanton ctx_setup(&ctx, KQUEUE_WRITE, 0);
127e22febffSanton
128e22febffSanton n = write(ctx.c_pipe[1], ctx.c_buf, ctx.c_bufsiz);
129e22febffSanton if (n == -1)
130e22febffSanton err(1, "write");
131e22febffSanton if ((size_t)n != ctx.c_bufsiz)
132e22febffSanton errx(1, "write: %ld != %zu", n, ctx.c_bufsiz);
133e22febffSanton
134e22febffSanton ctx_thread_start(&ctx);
135e22febffSanton
136e22febffSanton while (ctx_thread_alive(&ctx)) {
137e22febffSanton unsigned char c;
138e22febffSanton
139e22febffSanton n = read(ctx.c_pipe[0], &c, 1);
140e22febffSanton if (n == -1)
141e22febffSanton err(1, "read");
142dcab9128Santon if (n == 0)
143dcab9128Santon break;
144e22febffSanton if (n != 1)
145e22febffSanton errx(1, "read: %ld != 1", n);
146e22febffSanton }
147e22febffSanton
148e22febffSanton ctx_teardown(&ctx);
149e22febffSanton
150e22febffSanton return 0;
151e22febffSanton }
152e22febffSanton
153e22febffSanton /*
154e22febffSanton * XXX Verify kqueue write event.
155e22febffSanton */
156e22febffSanton int
test_kqueue_write_eof(void)157e22febffSanton test_kqueue_write_eof(void)
158e22febffSanton {
159e22febffSanton
160e22febffSanton return 0;
161e22febffSanton }
162e22febffSanton
163e22febffSanton static void
ctx_setup(struct context * ctx,enum kqueue_mode mode,int flags)164fc11865aSanton ctx_setup(struct context *ctx, enum kqueue_mode mode, int flags)
165e22febffSanton {
166e22febffSanton int error;
167e22febffSanton
168e22febffSanton ctx->c_mode = mode;
169e22febffSanton ctx->c_alive = 1;
170e22febffSanton
171fc11865aSanton if (flags) {
172fc11865aSanton if (pipe2(ctx->c_pipe, flags) == -1)
173fc11865aSanton err(1, "pipe");
174fc11865aSanton } else {
175e22febffSanton if (pipe(ctx->c_pipe) == -1)
176e22febffSanton err(1, "pipe");
177fc11865aSanton }
178e22febffSanton
179e22febffSanton ctx->c_kq = kqueue();
180e22febffSanton if (ctx->c_kq == -1)
181e22febffSanton err(1, "kqueue");
182e22febffSanton
183e22febffSanton ctx->c_bufsiz = PIPE_SIZE;
184e22febffSanton ctx->c_buf = malloc(ctx->c_bufsiz);
185e22febffSanton if (ctx->c_buf == NULL)
186e22febffSanton err(1, NULL);
187e22febffSanton
188e22febffSanton error = pthread_mutex_init(&ctx->c_mtx, NULL);
189e22febffSanton if (error)
190e22febffSanton errc(1, error, "pthread_mutex_init");
191e22febffSanton }
192e22febffSanton
193e22febffSanton static void
ctx_teardown(struct context * ctx)194e22febffSanton ctx_teardown(struct context *ctx)
195e22febffSanton {
196e22febffSanton int error;
197e22febffSanton
198e22febffSanton error = pthread_join(ctx->c_th, NULL);
199e22febffSanton if (error)
200e22febffSanton errc(1, error, "pthread_join");
201e22febffSanton
202e22febffSanton error = pthread_mutex_destroy(&ctx->c_mtx);
203e22febffSanton if (error)
204e22febffSanton errc(1, error, "pthread_mutex_destroy");
205e22febffSanton
206e22febffSanton free(ctx->c_buf);
207e22febffSanton
208e22febffSanton close(ctx->c_pipe[0]);
209e22febffSanton close(ctx->c_pipe[1]);
210e22febffSanton close(ctx->c_kq);
211e22febffSanton }
212e22febffSanton
213e22febffSanton static int
ctx_thread_alive(struct context * ctx)214e22febffSanton ctx_thread_alive(struct context *ctx)
215e22febffSanton {
216e22febffSanton int alive;
217e22febffSanton
218e22febffSanton ctx_lock(ctx);
219e22febffSanton alive = ctx->c_alive;
220e22febffSanton ctx_unlock(ctx);
221e22febffSanton return alive;
222e22febffSanton }
223e22febffSanton
224e22febffSanton static void
ctx_thread_start(struct context * ctx)225e22febffSanton ctx_thread_start(struct context *ctx)
226e22febffSanton {
227e22febffSanton int error;
228e22febffSanton
229e22febffSanton error = pthread_create(&ctx->c_th, NULL, kqueue_thread, ctx);
230e22febffSanton if (error)
231e22febffSanton errc(1, error, "pthread_create");
232e22febffSanton }
233e22febffSanton
234e22febffSanton static void
ctx_lock(struct context * ctx)235e22febffSanton ctx_lock(struct context *ctx)
236e22febffSanton {
237e22febffSanton int error;
238e22febffSanton
239e22febffSanton error = pthread_mutex_lock(&ctx->c_mtx);
240e22febffSanton if (error)
241e22febffSanton errc(1, error, "pthread_mutex_lock");
242e22febffSanton }
243e22febffSanton
244065bbfdcSanton static void
ctx_unlock(struct context * ctx)245065bbfdcSanton ctx_unlock(struct context *ctx)
246e22febffSanton {
247e22febffSanton int error;
248e22febffSanton
249e22febffSanton error = pthread_mutex_unlock(&ctx->c_mtx);
250e22febffSanton if (error)
251e22febffSanton errc(1, error, "pthread_mutex_unlock");
252e22febffSanton }
253e22febffSanton
254e22febffSanton static void *
kqueue_thread(void * arg)255e22febffSanton kqueue_thread(void *arg)
256e22febffSanton {
257e22febffSanton struct context *ctx = arg;
258e22febffSanton struct kevent kev;
259e22febffSanton int fd, filter, nevents;
260e22febffSanton
261e22febffSanton switch (ctx->c_mode) {
262e22febffSanton case KQUEUE_READ:
263e22febffSanton case KQUEUE_READ_EOF:
264e22febffSanton fd = ctx->c_pipe[0];
265e22febffSanton filter = EVFILT_READ;
266e22febffSanton break;
267e22febffSanton case KQUEUE_WRITE:
268e22febffSanton case KQUEUE_WRITE_EOF:
269e22febffSanton fd = ctx->c_pipe[1];
270e22febffSanton filter = EVFILT_WRITE;
271e22febffSanton break;
272e22febffSanton }
273e22febffSanton
274e22febffSanton EV_SET(&kev, fd, filter, EV_ADD, 0, 0, NULL);
275e22febffSanton nevents = kevent(ctx->c_kq, &kev, 1, NULL, 0, NULL);
276e22febffSanton if (nevents == -1)
277e22febffSanton err(1, "kevent");
278e22febffSanton nevents = kevent(ctx->c_kq, NULL, 0, &kev, 1, NULL);
279e22febffSanton if (nevents == -1)
280e22febffSanton err(1, "kevent");
281e22febffSanton if (nevents != 1)
282e22febffSanton errx(1, "kevent: %d != 1", nevents);
283e22febffSanton
284cfefc492Santon if ((int)kev.ident != fd)
285e22febffSanton errx(1, "kevent: ident");
286cfefc492Santon if (kev.filter != filter)
287e22febffSanton errx(1, "kevent: filter");
288e22febffSanton
289e22febffSanton switch (ctx->c_mode) {
290e22febffSanton case KQUEUE_READ_EOF:
291e22febffSanton case KQUEUE_WRITE_EOF:
292e22febffSanton if ((kev.flags & EV_EOF) == 0)
293e22febffSanton errx(1, "kevent: eof");
294e22febffSanton break;
295e22febffSanton default:
296e22febffSanton break;
297e22febffSanton }
298e22febffSanton
299e22febffSanton ctx_lock(ctx);
300e22febffSanton ctx->c_alive = 0;
301e22febffSanton ctx_unlock(ctx);
302e22febffSanton
303dcab9128Santon close(fd);
304dcab9128Santon
305e22febffSanton return NULL;
306e22febffSanton }
307