xref: /openbsd/regress/sys/kern/pipe/test-kqueue.c (revision d2aeeac5)
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