xref: /openbsd/lib/libevent/kqueue.c (revision 1770acb2)
1*1770acb2Smarkus /*	$OpenBSD: kqueue.c,v 1.10 2003/07/09 10:54:38 markus Exp $	*/
234fc9cdeSmickey 
3fd332320Sprovos /*
4fd332320Sprovos  * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
5fd332320Sprovos  * All rights reserved.
6fd332320Sprovos  *
7fd332320Sprovos  * Redistribution and use in source and binary forms, with or without
8fd332320Sprovos  * modification, are permitted provided that the following conditions
9fd332320Sprovos  * are met:
10fd332320Sprovos  * 1. Redistributions of source code must retain the above copyright
11fd332320Sprovos  *    notice, this list of conditions and the following disclaimer.
12fd332320Sprovos  * 2. Redistributions in binary form must reproduce the above copyright
13fd332320Sprovos  *    notice, this list of conditions and the following disclaimer in the
14fd332320Sprovos  *    documentation and/or other materials provided with the distribution.
15fd332320Sprovos  * 3. All advertising materials mentioning features or use of this software
16fd332320Sprovos  *    must display the following acknowledgement:
17fd332320Sprovos  *      This product includes software developed by Niels Provos.
18fd332320Sprovos  * 4. The name of the author may not be used to endorse or promote products
19fd332320Sprovos  *    derived from this software without specific prior written permission.
20fd332320Sprovos  *
21fd332320Sprovos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22fd332320Sprovos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23fd332320Sprovos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24fd332320Sprovos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25fd332320Sprovos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26fd332320Sprovos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27fd332320Sprovos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28fd332320Sprovos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29fd332320Sprovos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30fd332320Sprovos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31fd332320Sprovos  */
32*1770acb2Smarkus #ifdef HAVE_CONFIG_H
33fd332320Sprovos #include "config.h"
34*1770acb2Smarkus #endif
35fd332320Sprovos 
36fd332320Sprovos #include <sys/types.h>
37*1770acb2Smarkus #ifdef HAVE_SYS_TIME_H
38fd332320Sprovos #include <sys/time.h>
39*1770acb2Smarkus #else
40*1770acb2Smarkus #include <sys/_time.h>
41*1770acb2Smarkus #endif
42fd332320Sprovos #include <sys/queue.h>
43fd332320Sprovos #include <sys/event.h>
44fd332320Sprovos #include <signal.h>
45fd332320Sprovos #include <stdio.h>
46fd332320Sprovos #include <stdlib.h>
47ff33a3f0Sderaadt #include <string.h>
48fd332320Sprovos #include <unistd.h>
49fd332320Sprovos #include <errno.h>
50*1770acb2Smarkus #include <err.h>
51*1770acb2Smarkus #ifdef HAVE_INTTYPES_H
52*1770acb2Smarkus #include <inttypes.h>
53*1770acb2Smarkus #endif
54fd332320Sprovos 
55fd332320Sprovos #ifdef USE_LOG
56fd332320Sprovos #include "log.h"
57fd332320Sprovos #else
58fd332320Sprovos #define LOG_DBG(x)
59*1770acb2Smarkus #define log_error	warn
60*1770acb2Smarkus #endif
61*1770acb2Smarkus 
62*1770acb2Smarkus #ifdef HAVE_INTTYPES_H
63*1770acb2Smarkus #define INTPTR(x)	(intptr_t)x
64*1770acb2Smarkus #else
65*1770acb2Smarkus #define INTPTR(x)	x
66fd332320Sprovos #endif
67fd332320Sprovos 
68fd332320Sprovos #include "event.h"
69fd332320Sprovos 
70fd332320Sprovos extern struct event_list timequeue;
71fd332320Sprovos extern struct event_list eventqueue;
72fd332320Sprovos extern struct event_list addqueue;
73fd332320Sprovos 
74fd332320Sprovos #define EVLIST_X_KQINKERNEL	0x1000
75fd332320Sprovos 
76fd332320Sprovos #define NEVENT		64
77fd332320Sprovos 
78fd332320Sprovos struct kqop {
79fd332320Sprovos 	struct kevent *changes;
80fd332320Sprovos 	int nchanges;
81fd332320Sprovos 	struct kevent *events;
82fd332320Sprovos 	int nevents;
83fd332320Sprovos 	int kq;
84fd332320Sprovos } kqop;
85fd332320Sprovos 
86fd332320Sprovos void *kq_init	(void);
87fd332320Sprovos int kq_add	(void *, struct event *);
88fd332320Sprovos int kq_del	(void *, struct event *);
89fd332320Sprovos int kq_recalc	(void *, int);
90fd332320Sprovos int kq_dispatch	(void *, struct timeval *);
91fd332320Sprovos 
92759b8817Smickey const struct eventop kqops = {
93fd332320Sprovos 	"kqueue",
94fd332320Sprovos 	kq_init,
95fd332320Sprovos 	kq_add,
96fd332320Sprovos 	kq_del,
97fd332320Sprovos 	kq_recalc,
98fd332320Sprovos 	kq_dispatch
99fd332320Sprovos };
100fd332320Sprovos 
101fd332320Sprovos void *
102fd332320Sprovos kq_init(void)
103fd332320Sprovos {
104fd332320Sprovos 	int kq;
105fd332320Sprovos 
106fd332320Sprovos 	/* Disable kqueue when this environment variable is set */
107*1770acb2Smarkus 	if (!issetugid() && getenv("EVENT_NOKQUEUE"))
108fd332320Sprovos 		return (NULL);
109fd332320Sprovos 
110fd332320Sprovos 	memset(&kqop, 0, sizeof(kqop));
111fd332320Sprovos 
1129534f5ccSdavid 	/* Initialize the kernel queue */
113fd332320Sprovos 
114fd332320Sprovos 	if ((kq = kqueue()) == -1) {
115fd332320Sprovos 		log_error("kqueue");
116fd332320Sprovos 		return (NULL);
117fd332320Sprovos 	}
118fd332320Sprovos 
119fd332320Sprovos 	kqop.kq = kq;
120fd332320Sprovos 
1219534f5ccSdavid 	/* Initialize fields */
122fd332320Sprovos 	kqop.changes = malloc(NEVENT * sizeof(struct kevent));
123fd332320Sprovos 	if (kqop.changes == NULL)
124fd332320Sprovos 		return (NULL);
125fd332320Sprovos 	kqop.events = malloc(NEVENT * sizeof(struct kevent));
126fd332320Sprovos 	if (kqop.events == NULL) {
127fd332320Sprovos 		free (kqop.changes);
128fd332320Sprovos 		return (NULL);
129fd332320Sprovos 	}
130fd332320Sprovos 	kqop.nevents = NEVENT;
131fd332320Sprovos 
132fd332320Sprovos 	return (&kqop);
133fd332320Sprovos }
134fd332320Sprovos 
135fd332320Sprovos int
136fd332320Sprovos kq_recalc(void *arg, int max)
137fd332320Sprovos {
138fd332320Sprovos 	return (0);
139fd332320Sprovos }
140fd332320Sprovos 
141fd332320Sprovos int
142fd332320Sprovos kq_insert(struct kqop *kqop, struct kevent *kev)
143fd332320Sprovos {
144fd332320Sprovos 	int nevents = kqop->nevents;
145fd332320Sprovos 
146fd332320Sprovos 	if (kqop->nchanges == nevents) {
147fd332320Sprovos 		struct kevent *newchange;
148fd332320Sprovos 		struct kevent *newresult;
149fd332320Sprovos 
150fd332320Sprovos 		nevents *= 2;
151fd332320Sprovos 
152fd332320Sprovos 		newchange = realloc(kqop->changes,
153fd332320Sprovos 				    nevents * sizeof(struct kevent));
154fd332320Sprovos 		if (newchange == NULL) {
155*1770acb2Smarkus 			log_error("%s: malloc", __func__);
156fd332320Sprovos 			return (-1);
157fd332320Sprovos 		}
158fd332320Sprovos 		kqop->changes = newchange;
159fd332320Sprovos 
160fd332320Sprovos 		newresult = realloc(kqop->changes,
161fd332320Sprovos 				    nevents * sizeof(struct kevent));
162fd332320Sprovos 
163fd332320Sprovos 		/*
164fd332320Sprovos 		 * If we fail, we don't have to worry about freeing,
165fd332320Sprovos 		 * the next realloc will pick it up.
166fd332320Sprovos 		 */
167fd332320Sprovos 		if (newresult == NULL) {
168*1770acb2Smarkus 			log_error("%s: malloc", __func__);
169fd332320Sprovos 			return (-1);
170fd332320Sprovos 		}
171fd332320Sprovos 		kqop->events = newchange;
172fd332320Sprovos 
173fd332320Sprovos 		kqop->nevents = nevents;
174fd332320Sprovos 	}
175fd332320Sprovos 
176fd332320Sprovos 	memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
177fd332320Sprovos 
178*1770acb2Smarkus 	LOG_DBG((LOG_MISC, 70, "%s: fd %d %s%s",
179*1770acb2Smarkus 		 __func__, kev->ident,
180fd332320Sprovos 		 kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
181fd332320Sprovos 		 kev->flags == EV_DELETE ? " (del)" : ""));
182fd332320Sprovos 
183fd332320Sprovos 	return (0);
184fd332320Sprovos }
185fd332320Sprovos 
186fd332320Sprovos static void
187fd332320Sprovos kq_sighandler(int sig)
188fd332320Sprovos {
189fd332320Sprovos 	/* Do nothing here */
190fd332320Sprovos }
191fd332320Sprovos 
192fd332320Sprovos int
193fd332320Sprovos kq_dispatch(void *arg, struct timeval *tv)
194fd332320Sprovos {
195fd332320Sprovos 	struct kqop *kqop = arg;
196fd332320Sprovos 	struct kevent *changes = kqop->changes;
197fd332320Sprovos 	struct kevent *events = kqop->events;
198fd332320Sprovos 	struct event *ev;
199fd332320Sprovos 	struct timespec ts;
200fd332320Sprovos 	int i, res;
201fd332320Sprovos 
202fd332320Sprovos 	TIMEVAL_TO_TIMESPEC(tv, &ts);
203fd332320Sprovos 
204fd332320Sprovos 	res = kevent(kqop->kq, changes, kqop->nchanges,
205fd332320Sprovos 	    events, kqop->nevents, &ts);
206fd332320Sprovos 	kqop->nchanges = 0;
207fd332320Sprovos 	if (res == -1) {
208fd332320Sprovos 		if (errno != EINTR) {
209fd332320Sprovos 			log_error("kevent");
210fd332320Sprovos 			return (-1);
211fd332320Sprovos 		}
212fd332320Sprovos 
213fd332320Sprovos 		return (0);
214fd332320Sprovos 	}
215fd332320Sprovos 
216*1770acb2Smarkus 	LOG_DBG((LOG_MISC, 80, "%s: kevent reports %d", __func__, res));
217fd332320Sprovos 
218fd332320Sprovos 	for (i = 0; i < res; i++) {
219fd332320Sprovos 		int which = 0;
220fd332320Sprovos 
221fd332320Sprovos 		if (events[i].flags & EV_ERROR) {
222fd332320Sprovos 			/*
223fd332320Sprovos 			 * Error messages that can happen, when a delete fails.
224fd332320Sprovos 			 *   EBADF happens when the file discriptor has been
225fd332320Sprovos 			 *   closed,
226fd332320Sprovos 			 *   ENOENT when the file discriptor was closed and
227fd332320Sprovos 			 *   then reopened.
228fd332320Sprovos 			 * An error is also indicated when a callback deletes
229fd332320Sprovos 			 * an event we are still processing.  In that case
230fd332320Sprovos 			 * the data field is set to ENOENT.
231fd332320Sprovos 			 */
232fd332320Sprovos 			if (events[i].data == EBADF ||
233fd332320Sprovos 			    events[i].data == ENOENT)
234fd332320Sprovos 				continue;
235fd332320Sprovos 			return (-1);
236fd332320Sprovos 		}
237fd332320Sprovos 
238*1770acb2Smarkus 		ev = (struct event *)events[i].udata;
239fd332320Sprovos 
240fd332320Sprovos 		if (events[i].filter == EVFILT_READ) {
241fd332320Sprovos 			which |= EV_READ;
242fd332320Sprovos 		} else if (events[i].filter == EVFILT_WRITE) {
243fd332320Sprovos 			which |= EV_WRITE;
244fd332320Sprovos 		} else if (events[i].filter == EVFILT_SIGNAL) {
245fd332320Sprovos 			which |= EV_SIGNAL;
246*1770acb2Smarkus 		}
247fd332320Sprovos 
248fd332320Sprovos 		if (!which)
249fd332320Sprovos 			continue;
250fd332320Sprovos 
251*1770acb2Smarkus 		if (!(ev->ev_events & EV_PERSIST)) {
252fd332320Sprovos 			ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
253fd332320Sprovos 			event_del(ev);
254*1770acb2Smarkus 		}
255fd332320Sprovos 
256*1770acb2Smarkus 		event_active(ev, which,
257*1770acb2Smarkus 		    ev->ev_events & EV_SIGNAL ? events[i].data : 1);
258fd332320Sprovos 	}
259fd332320Sprovos 
260fd332320Sprovos 	return (0);
261fd332320Sprovos }
262fd332320Sprovos 
263fd332320Sprovos 
264fd332320Sprovos int
265fd332320Sprovos kq_add(void *arg, struct event *ev)
266fd332320Sprovos {
267fd332320Sprovos 	struct kqop *kqop = arg;
268fd332320Sprovos 	struct kevent kev;
269fd332320Sprovos 
270fd332320Sprovos 	if (ev->ev_events & EV_SIGNAL) {
271fd332320Sprovos 		int nsignal = EVENT_SIGNAL(ev);
272fd332320Sprovos 
273fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
274fd332320Sprovos 		kev.ident = nsignal;
275fd332320Sprovos 		kev.filter = EVFILT_SIGNAL;
276fd332320Sprovos 		kev.flags = EV_ADD;
277fd332320Sprovos 		if (!(ev->ev_events & EV_PERSIST))
278ddb00dd9Sitojun 			kev.flags |= EV_ONESHOT;
279*1770acb2Smarkus 		kev.udata = INTPTR(ev);
280fd332320Sprovos 
281fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
282fd332320Sprovos 			return (-1);
283fd332320Sprovos 
284fd332320Sprovos 		if (signal(nsignal, kq_sighandler) == SIG_ERR)
285fd332320Sprovos 			return (-1);
286fd332320Sprovos 
287fd332320Sprovos 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
288fd332320Sprovos 		return (0);
289fd332320Sprovos 	}
290fd332320Sprovos 
291fd332320Sprovos 	if (ev->ev_events & EV_READ) {
292fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
293fd332320Sprovos 		kev.ident = ev->ev_fd;
294fd332320Sprovos 		kev.filter = EVFILT_READ;
295e5c7daabSart 		kev.flags = EV_ADD;
296e5c7daabSart 		if (!(ev->ev_events & EV_PERSIST))
297ddb00dd9Sitojun 			kev.flags |= EV_ONESHOT;
298*1770acb2Smarkus 		kev.udata = INTPTR(ev);
299fd332320Sprovos 
300fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
301fd332320Sprovos 			return (-1);
302fd332320Sprovos 
303fd332320Sprovos 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
304fd332320Sprovos 	}
305fd332320Sprovos 
306fd332320Sprovos 	if (ev->ev_events & EV_WRITE) {
307fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
308fd332320Sprovos 		kev.ident = ev->ev_fd;
309fd332320Sprovos 		kev.filter = EVFILT_WRITE;
310e5c7daabSart 		kev.flags = EV_ADD;
311e5c7daabSart 		if (!(ev->ev_events & EV_PERSIST))
312ddb00dd9Sitojun 			kev.flags |= EV_ONESHOT;
313*1770acb2Smarkus 		kev.udata = INTPTR(ev);
314fd332320Sprovos 
315fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
316fd332320Sprovos 			return (-1);
317fd332320Sprovos 
318fd332320Sprovos 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
319fd332320Sprovos 	}
320fd332320Sprovos 
321fd332320Sprovos 	return (0);
322fd332320Sprovos }
323fd332320Sprovos 
324fd332320Sprovos int
325fd332320Sprovos kq_del(void *arg, struct event *ev)
326fd332320Sprovos {
327fd332320Sprovos 	struct kqop *kqop = arg;
328fd332320Sprovos 	struct kevent kev;
329fd332320Sprovos 
330fd332320Sprovos 	if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
331fd332320Sprovos 		return (0);
332fd332320Sprovos 
333fd332320Sprovos 	if (ev->ev_events & EV_SIGNAL) {
334fd332320Sprovos 		int nsignal = EVENT_SIGNAL(ev);
335fd332320Sprovos 
336fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
337fd332320Sprovos 		kev.ident = (int)signal;
338fd332320Sprovos 		kev.filter = EVFILT_SIGNAL;
339fd332320Sprovos 		kev.flags = EV_DELETE;
340fd332320Sprovos 
341fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
342fd332320Sprovos 			return (-1);
343fd332320Sprovos 
344fd332320Sprovos 		if (signal(nsignal, SIG_DFL) == SIG_ERR)
345fd332320Sprovos 			return (-1);
346fd332320Sprovos 
347fd332320Sprovos 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
348fd332320Sprovos 		return (0);
349fd332320Sprovos 	}
350fd332320Sprovos 
351fd332320Sprovos 	if (ev->ev_events & EV_READ) {
352fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
353fd332320Sprovos 		kev.ident = ev->ev_fd;
354fd332320Sprovos 		kev.filter = EVFILT_READ;
355fd332320Sprovos 		kev.flags = EV_DELETE;
356fd332320Sprovos 
357fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
358fd332320Sprovos 			return (-1);
359fd332320Sprovos 
360fd332320Sprovos 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
361fd332320Sprovos 	}
362fd332320Sprovos 
363fd332320Sprovos 	if (ev->ev_events & EV_WRITE) {
364fd332320Sprovos  		memset(&kev, 0, sizeof(kev));
365fd332320Sprovos 		kev.ident = ev->ev_fd;
366fd332320Sprovos 		kev.filter = EVFILT_WRITE;
367fd332320Sprovos 		kev.flags = EV_DELETE;
368fd332320Sprovos 
369fd332320Sprovos 		if (kq_insert(kqop, &kev) == -1)
370fd332320Sprovos 			return (-1);
371fd332320Sprovos 
372fd332320Sprovos 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
373fd332320Sprovos 	}
374fd332320Sprovos 
375fd332320Sprovos 	return (0);
376fd332320Sprovos }
377