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