xref: /libevent-2.1.12/kqueue.c (revision 9d2401ff)
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. 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 #include "config.h"
33 
34 #include <sys/types.h>
35 #ifdef HAVE_SYS_TIME_H
36 #include <sys/time.h>
37 #else
38 #include <sys/_time.h>
39 #endif
40 #include <sys/queue.h>
41 #include <sys/event.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <errno.h>
48 
49 #ifdef USE_LOG
50 #include "log.h"
51 #else
52 #define LOG_DBG(x)
53 #define log_error(x)	perror(x)
54 #endif
55 
56 #include "event.h"
57 
58 extern struct event_list timequeue;
59 extern struct event_list eventqueue;
60 extern struct event_list addqueue;
61 
62 #define EVLIST_X_KQINKERNEL	0x1000
63 
64 #define NEVENT		64
65 
66 struct kqop {
67 	struct kevent *changes;
68 	int nchanges;
69 	struct kevent *events;
70 	int nevents;
71 	int kq;
72 } kqop;
73 
74 void *kq_init	(void);
75 int kq_add	(void *, struct event *);
76 int kq_del	(void *, struct event *);
77 int kq_recalc	(void *, int);
78 int kq_dispatch	(void *, struct timeval *);
79 
80 struct eventop kqops = {
81 	"kqueue",
82 	kq_init,
83 	kq_add,
84 	kq_del,
85 	kq_recalc,
86 	kq_dispatch
87 };
88 
89 void *
90 kq_init(void)
91 {
92 	int kq;
93 
94 	/* Disable kqueue when this environment variable is set */
95 	if (getenv("EVENT_NOKQUEUE"))
96 		return (NULL);
97 
98 	memset(&kqop, 0, sizeof(kqop));
99 
100 	/* Initalize the kernel queue */
101 
102 	if ((kq = kqueue()) == -1) {
103 		log_error("kqueue");
104 		return (NULL);
105 	}
106 
107 	kqop.kq = kq;
108 
109 	/* Initalize fields */
110 	kqop.changes = malloc(NEVENT * sizeof(struct kevent));
111 	if (kqop.changes == NULL)
112 		return (NULL);
113 	kqop.events = malloc(NEVENT * sizeof(struct kevent));
114 	if (kqop.events == NULL) {
115 		free (kqop.changes);
116 		return (NULL);
117 	}
118 	kqop.nevents = NEVENT;
119 
120 	return (&kqop);
121 }
122 
123 int
124 kq_recalc(void *arg, int max)
125 {
126 	return (0);
127 }
128 
129 int
130 kq_insert(struct kqop *kqop, struct kevent *kev)
131 {
132 	int nevents = kqop->nevents;
133 
134 	if (kqop->nchanges == nevents) {
135 		struct kevent *newchange;
136 		struct kevent *newresult;
137 
138 		nevents *= 2;
139 
140 		newchange = realloc(kqop->changes,
141 				    nevents * sizeof(struct kevent));
142 		if (newchange == NULL) {
143 			log_error(__FUNCTION__": malloc");
144 			return (-1);
145 		}
146 		kqop->changes = newchange;
147 
148 		newresult = realloc(kqop->changes,
149 				    nevents * sizeof(struct kevent));
150 
151 		/*
152 		 * If we fail, we don't have to worry about freeing,
153 		 * the next realloc will pick it up.
154 		 */
155 		if (newresult == NULL) {
156 			log_error(__FUNCTION__": malloc");
157 			return (-1);
158 		}
159 		kqop->events = newchange;
160 
161 		kqop->nevents = nevents;
162 	}
163 
164 	memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
165 
166 	LOG_DBG((LOG_MISC, 70, __FUNCTION__": fd %d %s%s",
167 		 kev->ident,
168 		 kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
169 		 kev->flags == EV_DELETE ? " (del)" : ""));
170 
171 	return (0);
172 }
173 
174 static void
175 kq_sighandler(int sig)
176 {
177 	/* Do nothing here */
178 }
179 
180 int
181 kq_dispatch(void *arg, struct timeval *tv)
182 {
183 	struct kqop *kqop = arg;
184 	struct kevent *changes = kqop->changes;
185 	struct kevent *events = kqop->events;
186 	struct event *ev;
187 	struct timespec ts;
188 	int i, res;
189 
190 	TIMEVAL_TO_TIMESPEC(tv, &ts);
191 
192 	res = kevent(kqop->kq, changes, kqop->nchanges,
193 		     events, kqop->nevents, &ts);
194 	kqop->nchanges = 0;
195 	if (res == -1) {
196 		if (errno != EINTR) {
197 			log_error("kevent");
198 			return (-1);
199 		}
200 
201 		return (0);
202 	}
203 
204 	LOG_DBG((LOG_MISC, 80, __FUNCTION__": kevent reports %d", res));
205 
206 	for (i = 0; i < res; i++) {
207 		int which = 0;
208 
209 		if (events[i].flags & EV_ERROR) {
210 			/*
211 			 * Error messages that can happen, when a delete fails.
212 			 *   EBADF happens when the file discriptor has been
213 			 *   closed,
214 			 *   ENOENT when the file discriptor was closed and
215 			 *   then reopened.
216 			 * An error is also indicated when a callback deletes
217 			 * an event we are still processing.  In that case
218 			 * the data field is set to ENOENT.
219 			 */
220 			if (events[i].data == EBADF ||
221 			    events[i].data == ENOENT)
222 				continue;
223 			return (-1);
224 		}
225 
226 		ev = events[i].udata;
227 
228 		if (events[i].filter == EVFILT_READ) {
229 			which |= EV_READ;
230 		} else if (events[i].filter == EVFILT_WRITE) {
231 			which |= EV_WRITE;
232 		} else if (events[i].filter == EVFILT_SIGNAL) {
233 			which |= EV_SIGNAL;
234 		} else
235 			events[i].filter = 0;
236 
237 		if (!which)
238 			continue;
239 
240 		event_active(ev, which,
241 		    ev->ev_events & EV_SIGNAL ? events[i].data : 1);
242 	}
243 
244 	for (i = 0; i < res; i++) {
245 		/* XXX */
246 		int ncalls, evres;
247 
248 		if (events[i].flags & EV_ERROR || events[i].filter == NULL)
249 			continue;
250 
251 		ev = events[i].udata;
252 		if (ev->ev_events & EV_PERSIST)
253 			continue;
254 
255 		ncalls = 0;
256 		if (ev->ev_flags & EVLIST_ACTIVE) {
257 			ncalls = ev->ev_ncalls;
258 			evres = ev->ev_res;
259 		}
260 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
261 		event_del(ev);
262 
263 		if (ncalls)
264 			event_active(ev, evres, ncalls);
265 	}
266 
267 	return (0);
268 }
269 
270 
271 int
272 kq_add(void *arg, struct event *ev)
273 {
274 	struct kqop *kqop = arg;
275 	struct kevent kev;
276 
277 	if (ev->ev_events & EV_SIGNAL) {
278 		int nsignal = EVENT_SIGNAL(ev);
279 
280  		memset(&kev, 0, sizeof(kev));
281 		kev.ident = nsignal;
282 		kev.filter = EVFILT_SIGNAL;
283 		kev.flags = EV_ADD;
284 		if (!(ev->ev_events & EV_PERSIST))
285 			kev.flags |= EV_ONESHOT;
286 		kev.udata = ev;
287 
288 		if (kq_insert(kqop, &kev) == -1)
289 			return (-1);
290 
291 		if (signal(nsignal, kq_sighandler) == SIG_ERR)
292 			return (-1);
293 
294 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
295 		return (0);
296 	}
297 
298 	if (ev->ev_events & EV_READ) {
299  		memset(&kev, 0, sizeof(kev));
300 		kev.ident = ev->ev_fd;
301 		kev.filter = EVFILT_READ;
302 		kev.flags = EV_ADD;
303 		if (!(ev->ev_events & EV_PERSIST))
304 			kev.flags |= EV_ONESHOT;
305 		kev.udata = ev;
306 
307 		if (kq_insert(kqop, &kev) == -1)
308 			return (-1);
309 
310 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
311 	}
312 
313 	if (ev->ev_events & EV_WRITE) {
314  		memset(&kev, 0, sizeof(kev));
315 		kev.ident = ev->ev_fd;
316 		kev.filter = EVFILT_WRITE;
317 		kev.flags = EV_ADD;
318 		if (!(ev->ev_events & EV_PERSIST))
319 			kev.flags |= EV_ONESHOT;
320 		kev.udata = ev;
321 
322 		if (kq_insert(kqop, &kev) == -1)
323 			return (-1);
324 
325 		ev->ev_flags |= EVLIST_X_KQINKERNEL;
326 	}
327 
328 	return (0);
329 }
330 
331 int
332 kq_del(void *arg, struct event *ev)
333 {
334 	struct kqop *kqop = arg;
335 	struct kevent kev;
336 
337 	if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
338 		return (0);
339 
340 	if (ev->ev_events & EV_SIGNAL) {
341 		int nsignal = EVENT_SIGNAL(ev);
342 
343  		memset(&kev, 0, sizeof(kev));
344 		kev.ident = (int)signal;
345 		kev.filter = EVFILT_SIGNAL;
346 		kev.flags = EV_DELETE;
347 
348 		if (kq_insert(kqop, &kev) == -1)
349 			return (-1);
350 
351 		if (signal(nsignal, SIG_DFL) == SIG_ERR)
352 			return (-1);
353 
354 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
355 		return (0);
356 	}
357 
358 	if (ev->ev_events & EV_READ) {
359  		memset(&kev, 0, sizeof(kev));
360 		kev.ident = ev->ev_fd;
361 		kev.filter = EVFILT_READ;
362 		kev.flags = EV_DELETE;
363 
364 		if (kq_insert(kqop, &kev) == -1)
365 			return (-1);
366 
367 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
368 	}
369 
370 	if (ev->ev_events & EV_WRITE) {
371  		memset(&kev, 0, sizeof(kev));
372 		kev.ident = ev->ev_fd;
373 		kev.filter = EVFILT_WRITE;
374 		kev.flags = EV_DELETE;
375 
376 		if (kq_insert(kqop, &kev) == -1)
377 			return (-1);
378 
379 		ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
380 	}
381 
382 	return (0);
383 }
384