cf0503c972d0cae22f92226e74f5c3b19c59bda1
[dragonfly.git] / contrib / libevent / kqueue.c
1 /*      $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art 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. 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 #ifdef HAVE_INTTYPES_H
48 #include <inttypes.h>
49 #endif
50
51 /* Some platforms apparently define the udata field of struct kevent as
52  * ntptr_t, whereas others define it as void*.  There doesn't seem to be an
53  * easy way to tell them apart via autoconf, so we need to use OS macros. */
54 #define PTR_TO_UDATA(x) ((void *)(x))
55
56 #include "event.h"
57 #include "log.h"
58
59 #define EVLIST_X_KQINKERNEL     0x1000
60
61 #define NEVENT          64
62
63 struct kqop {
64         struct kevent *changes;
65         int nchanges;
66         struct kevent *events;
67         int nevents;
68         int kq;
69 };
70
71 void *kq_init   (struct event_base *);
72 int kq_add      (void *, struct event *);
73 int kq_del      (void *, struct event *);
74 int kq_recalc   (struct event_base *, void *, int);
75 int kq_dispatch (struct event_base *, void *, struct timeval *);
76 int kq_insert   (struct kqop *, struct kevent *);
77 void kq_dealloc (struct event_base *, void *);
78
79 const struct eventop kqops = {
80         "kqueue",
81         kq_init,
82         kq_add,
83         kq_del,
84         kq_recalc,
85         kq_dispatch,
86         kq_dealloc
87 };
88
89 void *
90 kq_init(struct event_base *base)
91 {
92         int kq;
93         struct kqop *kqueueop;
94
95         /* Disable kqueue when this environment variable is set */
96         if (getenv("EVENT_NOKQUEUE"))
97                 return (NULL);
98
99         if (!(kqueueop = calloc(1, sizeof(struct kqop))))
100                 return (NULL);
101
102         /* Initalize the kernel queue */
103         
104         if ((kq = kqueue()) == -1) {
105                 event_warn("kqueue");
106                 free (kqueueop);
107                 return (NULL);
108         }
109
110         kqueueop->kq = kq;
111
112         /* Initalize fields */
113         kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
114         if (kqueueop->changes == NULL) {
115                 free (kqueueop);
116                 return (NULL);
117         }
118         kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
119         if (kqueueop->events == NULL) {
120                 free (kqueueop->changes);
121                 free (kqueueop);
122                 return (NULL);
123         }
124         kqueueop->nevents = NEVENT;
125
126         /* Check for Mac OS X kqueue bug. */
127         kqueueop->changes[0].ident = -1;
128         kqueueop->changes[0].filter = EVFILT_READ;
129         kqueueop->changes[0].flags = EV_ADD;
130         /* 
131          * If kqueue works, then kevent will succeed, and it will
132          * stick an error in events[0].  If kqueue is broken, then
133          * kevent will fail.
134          */
135         if (kevent(kq,
136                 kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
137             kqueueop->events[0].ident != -1 ||
138             kqueueop->events[0].flags != EV_ERROR) {
139                 event_warn("%s: detected broken kqueue; not using.", __func__);
140                 free(kqueueop->changes);
141                 free(kqueueop->events);
142                 free(kqueueop);
143                 close(kq);
144                 return (NULL);
145         }
146
147         return (kqueueop);
148 }
149
150 int
151 kq_recalc(struct event_base *base, void *arg, int max)
152 {
153         return (0);
154 }
155
156 int
157 kq_insert(struct kqop *kqop, struct kevent *kev)
158 {
159         int nevents = kqop->nevents;
160
161         if (kqop->nchanges == nevents) {
162                 struct kevent *newchange;
163                 struct kevent *newresult;
164
165                 nevents *= 2;
166
167                 newchange = realloc(kqop->changes,
168                                     nevents * sizeof(struct kevent));
169                 if (newchange == NULL) {
170                         event_warn("%s: malloc", __func__);
171                         return (-1);
172                 }
173                 kqop->changes = newchange;
174
175                 newresult = realloc(kqop->events,
176                                     nevents * sizeof(struct kevent));
177
178                 /*
179                  * If we fail, we don't have to worry about freeing,
180                  * the next realloc will pick it up.
181                  */
182                 if (newresult == NULL) {
183                         event_warn("%s: malloc", __func__);
184                         return (-1);
185                 }
186                 kqop->events = newresult;
187
188                 kqop->nevents = nevents;
189         }
190
191         memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
192
193         event_debug(("%s: fd %d %s%s",
194                  __func__, kev->ident, 
195                  kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
196                  kev->flags == EV_DELETE ? " (del)" : ""));
197
198         return (0);
199 }
200
201 static void
202 kq_sighandler(int sig)
203 {
204         /* Do nothing here */
205 }
206
207 int
208 kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
209 {
210         struct kqop *kqop = arg;
211         struct kevent *changes = kqop->changes;
212         struct kevent *events = kqop->events;
213         struct event *ev;
214         struct timespec ts, *ts_p = NULL;
215         int i, res;
216
217         if (tv != NULL) {
218                 TIMEVAL_TO_TIMESPEC(tv, &ts);
219                 ts_p = &ts;
220         }
221
222         res = kevent(kqop->kq, changes, kqop->nchanges,
223             events, kqop->nevents, ts_p);
224         kqop->nchanges = 0;
225         if (res == -1) {
226                 if (errno != EINTR) {
227                         event_warn("kevent");
228                         return (-1);
229                 }
230
231                 return (0);
232         }
233
234         event_debug(("%s: kevent reports %d", __func__, res));
235
236         for (i = 0; i < res; i++) {
237                 int which = 0;
238
239                 if (events[i].flags & EV_ERROR) {
240                         /* 
241                          * Error messages that can happen, when a delete fails.
242                          *   EBADF happens when the file discriptor has been
243                          *   closed,
244                          *   ENOENT when the file discriptor was closed and
245                          *   then reopened.
246                          *   EINVAL for some reasons not understood; EINVAL
247                          *   should not be returned ever; but FreeBSD does :-\
248                          * An error is also indicated when a callback deletes
249                          * an event we are still processing.  In that case
250                          * the data field is set to ENOENT.
251                          */
252                         if (events[i].data == EBADF ||
253                             events[i].data == EINVAL ||
254                             events[i].data == ENOENT)
255                                 continue;
256                         errno = events[i].data;
257                         return (-1);
258                 }
259
260                 ev = (struct event *)events[i].udata;
261
262                 if (events[i].filter == EVFILT_READ) {
263                         which |= EV_READ;
264                 } else if (events[i].filter == EVFILT_WRITE) {
265                         which |= EV_WRITE;
266                 } else if (events[i].filter == EVFILT_SIGNAL) {
267                         which |= EV_SIGNAL;
268                 }
269
270                 if (!which)
271                         continue;
272
273                 if (!(ev->ev_events & EV_PERSIST))
274                         event_del(ev);
275
276                 event_active(ev, which,
277                     ev->ev_events & EV_SIGNAL ? events[i].data : 1);
278         }
279
280         return (0);
281 }
282
283
284 int
285 kq_add(void *arg, struct event *ev)
286 {
287         struct kqop *kqop = arg;
288         struct kevent kev;
289
290         if (ev->ev_events & EV_SIGNAL) {
291                 int nsignal = EVENT_SIGNAL(ev);
292
293                 memset(&kev, 0, sizeof(kev));
294                 kev.ident = nsignal;
295                 kev.filter = EVFILT_SIGNAL;
296                 kev.flags = EV_ADD;
297                 if (!(ev->ev_events & EV_PERSIST))
298                         kev.flags |= EV_ONESHOT;
299                 kev.udata = PTR_TO_UDATA(ev);
300                 
301                 if (kq_insert(kqop, &kev) == -1)
302                         return (-1);
303
304                 if (signal(nsignal, kq_sighandler) == SIG_ERR)
305                         return (-1);
306
307                 ev->ev_flags |= EVLIST_X_KQINKERNEL;
308                 return (0);
309         }
310
311         if (ev->ev_events & EV_READ) {
312                 memset(&kev, 0, sizeof(kev));
313                 kev.ident = ev->ev_fd;
314                 kev.filter = EVFILT_READ;
315 #ifdef NOTE_EOF
316                 /* Make it behave like select() and poll() */
317                 kev.fflags = NOTE_EOF;
318 #endif
319                 kev.flags = EV_ADD;
320                 if (!(ev->ev_events & EV_PERSIST))
321                         kev.flags |= EV_ONESHOT;
322                 kev.udata = PTR_TO_UDATA(ev);
323                 
324                 if (kq_insert(kqop, &kev) == -1)
325                         return (-1);
326
327                 ev->ev_flags |= EVLIST_X_KQINKERNEL;
328         }
329
330         if (ev->ev_events & EV_WRITE) {
331                 memset(&kev, 0, sizeof(kev));
332                 kev.ident = ev->ev_fd;
333                 kev.filter = EVFILT_WRITE;
334                 kev.flags = EV_ADD;
335                 if (!(ev->ev_events & EV_PERSIST))
336                         kev.flags |= EV_ONESHOT;
337                 kev.udata = PTR_TO_UDATA(ev);
338                 
339                 if (kq_insert(kqop, &kev) == -1)
340                         return (-1);
341
342                 ev->ev_flags |= EVLIST_X_KQINKERNEL;
343         }
344
345         return (0);
346 }
347
348 int
349 kq_del(void *arg, struct event *ev)
350 {
351         struct kqop *kqop = arg;
352         struct kevent kev;
353
354         if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
355                 return (0);
356
357         if (ev->ev_events & EV_SIGNAL) {
358                 int nsignal = EVENT_SIGNAL(ev);
359
360                 memset(&kev, 0, sizeof(kev));
361                 kev.ident = nsignal;
362                 kev.filter = EVFILT_SIGNAL;
363                 kev.flags = EV_DELETE;
364                 
365                 if (kq_insert(kqop, &kev) == -1)
366                         return (-1);
367
368                 if (signal(nsignal, SIG_DFL) == SIG_ERR)
369                         return (-1);
370
371                 ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
372                 return (0);
373         }
374
375         if (ev->ev_events & EV_READ) {
376                 memset(&kev, 0, sizeof(kev));
377                 kev.ident = ev->ev_fd;
378                 kev.filter = EVFILT_READ;
379                 kev.flags = EV_DELETE;
380                 
381                 if (kq_insert(kqop, &kev) == -1)
382                         return (-1);
383
384                 ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
385         }
386
387         if (ev->ev_events & EV_WRITE) {
388                 memset(&kev, 0, sizeof(kev));
389                 kev.ident = ev->ev_fd;
390                 kev.filter = EVFILT_WRITE;
391                 kev.flags = EV_DELETE;
392                 
393                 if (kq_insert(kqop, &kev) == -1)
394                         return (-1);
395
396                 ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
397         }
398
399         return (0);
400 }
401
402 void
403 kq_dealloc(struct event_base *base, void *arg)
404 {
405         struct kqop *kqop = arg;
406
407         if (kqop->changes)
408                 free(kqop->changes);
409         if (kqop->events)
410                 free(kqop->events);
411         if (kqop->kq)
412                 close(kqop->kq);
413         memset(kqop, 0, sizeof(struct kqop));
414         free(kqop);
415 }