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