source: trunk/third-party/libevent/kqueue.c @ 9949

Last change on this file since 9949 was 9949, checked in by charles, 12 years ago

(trunk libT) turn on DEBUG_KQUEUE_CHANGEIDX debugging

File size: 15.1 KB
Line 
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#define _GNU_SOURCE 1
34
35#include <sys/types.h>
36#ifdef HAVE_SYS_TIME_H
37#include <sys/time.h>
38#else
39#include <sys/_libevent_time.h>
40#endif
41#include <sys/queue.h>
42#include <sys/event.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <errno.h>
49#include <assert.h>
50#ifdef HAVE_INTTYPES_H
51#include <inttypes.h>
52#endif
53
54#ifndef DEBUG_KQUEUE_CHANGEIDX
55#define DEBUG_KQUEUE_CHANGEIDX
56
57/* Some platforms apparently define the udata field of struct kevent as
58 * intptr_t, whereas others define it as void*.  There doesn't seem to be an
59 * easy way to tell them apart via autoconf, so we need to use OS macros. */
60#if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__)
61#define PTR_TO_UDATA(x) ((intptr_t)(x))
62#else
63#define PTR_TO_UDATA(x) (x)
64#endif
65
66#include "event.h"
67#include "event-internal.h"
68#include "log.h"
69#include "evsignal.h"
70
71#define EVLIST_X_KQINKERNEL     0x1000
72
73#define NEVENT          64
74
75/* per-fd information tracked when using the kqueue backend. */
76struct kqidx {
77        /* Index in kqop->changes to the last attempt to add or delete
78         * EVFILT_READ on this fd.  This value is cleared on dispatch by
79         * setting it to -1 */
80        int read_idx;
81        /* Index in kqop->changes to the last attempt to add or delete
82         * EVFILT_READ on this fd. */
83        int write_idx;
84};
85
86struct kqop {
87        struct kevent *changes;
88        int nchanges;
89        struct kevent *events;
90        struct event_list evsigevents[NSIG];
91        int nevents;
92        int kq;
93        pid_t pid;
94
95        struct kqidx *change_idx;
96        int change_idx_size;
97};
98
99static void *kq_init    (struct event_base *);
100static int kq_add       (void *, struct event *);
101static int kq_del       (void *, struct event *);
102static int kq_dispatch  (struct event_base *, void *, struct timeval *);
103static int kq_insert    (struct kqop *, struct kevent *);
104static void kq_dealloc (struct event_base *, void *);
105
106const struct eventop kqops = {
107        "kqueue",
108        kq_init,
109        kq_add,
110        kq_del,
111        kq_dispatch,
112        kq_dealloc,
113        1 /* need reinit */
114};
115
116static void *
117kq_init(struct event_base *base)
118{
119        int i, kq;
120        struct kqop *kqueueop;
121
122        /* Disable kqueue when this environment variable is set */
123        if (evutil_getenv("EVENT_NOKQUEUE"))
124                return (NULL);
125
126        if (!(kqueueop = calloc(1, sizeof(struct kqop))))
127                return (NULL);
128
129        /* Initalize the kernel queue */
130       
131        if ((kq = kqueue()) == -1) {
132                event_warn("kqueue");
133                free (kqueueop);
134                return (NULL);
135        }
136
137        kqueueop->kq = kq;
138
139        kqueueop->pid = getpid();
140
141        /* Initalize fields */
142        kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
143        if (kqueueop->changes == NULL) {
144                free (kqueueop);
145                return (NULL);
146        }
147        kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
148        if (kqueueop->events == NULL) {
149                free (kqueueop->changes);
150                free (kqueueop);
151                return (NULL);
152        }
153        kqueueop->nevents = NEVENT;
154
155        kqueueop->change_idx = NULL;
156        kqueueop->change_idx_size = 0;
157
158        /* we need to keep track of multiple events per signal */
159        for (i = 0; i < NSIG; ++i) {
160                TAILQ_INIT(&kqueueop->evsigevents[i]);
161        }
162
163        /* Check for Mac OS X kqueue bug. */
164        memset(&kqueueop->changes[0], 0, sizeof kqueueop->changes[0]);
165        kqueueop->changes[0].ident = -1;
166        kqueueop->changes[0].filter = EVFILT_READ;
167        kqueueop->changes[0].flags = EV_ADD;
168        /*
169         * If kqueue works, then kevent will succeed, and it will
170         * stick an error in events[0].  If kqueue is broken, then
171         * kevent will fail.
172         */
173        if (kevent(kq,
174                kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
175            kqueueop->events[0].ident != -1 ||
176            kqueueop->events[0].flags != EV_ERROR) {
177                event_warn("%s: detected broken kqueue; not using.", __func__);
178                free(kqueueop->changes);
179                free(kqueueop->events);
180                free(kqueueop);
181                close(kq);
182                return (NULL);
183        }
184
185        return (kqueueop);
186}
187
188static int
189kq_insert(struct kqop *kqop, struct kevent *kev)
190{
191        int nevents = kqop->nevents;
192
193        if (kqop->nchanges == nevents) {
194                struct kevent *newchange;
195                struct kevent *newresult;
196
197                nevents *= 2;
198
199                newchange = realloc(kqop->changes,
200                                    nevents * sizeof(struct kevent));
201                if (newchange == NULL) {
202                        event_warn("%s: malloc", __func__);
203                        return (-1);
204                }
205                kqop->changes = newchange;
206
207                newresult = realloc(kqop->events,
208                                    nevents * sizeof(struct kevent));
209
210                /*
211                 * If we fail, we don't have to worry about freeing,
212                 * the next realloc will pick it up.
213                 */
214                if (newresult == NULL) {
215                        event_warn("%s: malloc", __func__);
216                        return (-1);
217                }
218                kqop->events = newresult;
219
220                kqop->nevents = nevents;
221        }
222
223        memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
224
225        event_debug(("%s: fd %d %s%s",
226                __func__, (int)kev->ident, 
227                kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
228                kev->flags == EV_DELETE ? " (del)" : ""));
229
230        return (0);
231}
232
233static void
234kq_sighandler(int sig)
235{
236        /* Do nothing here */
237}
238
239#ifdef DEBUG_KQUEUE_CHANGEIDX
240static void
241changes_ok(struct kqop *kqop)
242{
243        struct kevent *changes = kqop->changes;
244        int i;
245
246        for (i = 0; i < kqop->nchanges; ++i) {
247                int fd = changes[i].ident;
248                if (changes[i].filter == EVFILT_READ) {
249                        assert(kqop->change_idx[fd].read_idx == i);
250                } else if (changes[i].filter == EVFILT_WRITE) {
251                        assert(kqop->change_idx[fd].write_idx == i);
252                }
253        }
254
255        for (i = 0; i < kqop->change_idx_size; ++i) {
256                struct kevent *c;
257                int idx;
258                if (kqop->change_idx[i].read_idx >= 0) {
259                        idx = kqop->change_idx[i].read_idx;
260                        assert(idx < kqop->nchanges);
261                        c = &kqop->changes[idx];
262                        assert(c->ident == i);
263                        assert(c->filter == EVFILT_READ);
264                }
265                if (kqop->change_idx[i].write_idx >= 0) {
266                        idx = kqop->change_idx[i].write_idx;
267                        assert(idx < kqop->nchanges);
268                        c = &kqop->changes[idx];
269
270                        c = &kqop->changes[kqop->change_idx[i].write_idx];
271                        assert(c->ident == i);
272                        assert(c->filter == EVFILT_WRITE);
273                }
274        }
275}
276#else
277#define changes_ok(kqop) ((void)0)
278#endif
279
280static int
281kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
282{
283        struct kqop *kqop = arg;
284        struct kevent *changes = kqop->changes;
285        struct kevent *events = kqop->events;
286        struct event *ev;
287        struct timespec ts, *ts_p = NULL;
288        int i, res;
289
290        if (tv != NULL) {
291                TIMEVAL_TO_TIMESPEC(tv, &ts);
292                ts_p = &ts;
293        }
294
295        changes_ok(kqop);
296        for (i = 0; i < kqop->nchanges; ++i) {
297                int fd = changes[i].ident;
298                if (changes[i].filter == EVFILT_READ) {
299                        kqop->change_idx[fd].read_idx = -1;
300                } else if (changes[i].filter == EVFILT_WRITE) {
301                        kqop->change_idx[fd].write_idx = -1;
302                }
303        }
304
305        res = kevent(kqop->kq, changes, kqop->nchanges,
306            events, kqop->nevents, ts_p);
307        kqop->nchanges = 0;
308        if (res == -1) {
309                if (errno != EINTR) {
310                        event_warn("kevent");
311                        return (-1);
312                }
313
314                return (0);
315        }
316
317        event_debug(("%s: kevent reports %d", __func__, res));
318
319        for (i = 0; i < res; i++) {
320                int which = 0;
321
322                if (events[i].flags & EV_ERROR) {
323                        /*
324                         * Error messages that can happen, when a delete fails.
325                         *   EBADF happens when the file discriptor has been
326                         *   closed,
327                         *   ENOENT when the file discriptor was closed and
328                         *   then reopened.
329                         *   EINVAL for some reasons not understood; EINVAL
330                         *   should not be returned ever; but FreeBSD does :-\
331                         * An error is also indicated when a callback deletes
332                         * an event we are still processing.  In that case
333                         * the data field is set to ENOENT.
334                         */
335                        if (events[i].data == EBADF ||
336                            events[i].data == EINVAL ||
337                            events[i].data == ENOENT)
338                                continue;
339                        errno = events[i].data;
340                        return (-1);
341                }
342
343                if (events[i].filter == EVFILT_READ) {
344                        which |= EV_READ;
345                } else if (events[i].filter == EVFILT_WRITE) {
346                        which |= EV_WRITE;
347                } else if (events[i].filter == EVFILT_SIGNAL) {
348                        which |= EV_SIGNAL;
349                }
350
351                if (!which)
352                        continue;
353
354                if (events[i].filter == EVFILT_SIGNAL) {
355                        struct event_list *head =
356                            (struct event_list *)events[i].udata;
357                        TAILQ_FOREACH(ev, head, ev_signal_next) {
358                                event_active(ev, which, events[i].data);
359                        }
360                } else {
361                        ev = (struct event *)events[i].udata;
362
363                        if (!(ev->ev_events & EV_PERSIST))
364                                ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
365
366                        event_active(ev, which, 1);
367                }
368        }
369        changes_ok(kqop);
370        return (0);
371}
372
373static struct kqidx *
374kqidx_get_for_fd(struct kqop *kqop, int fd)
375{
376        if (fd >= kqop->change_idx_size) {
377                int i;
378                int new_size = kqop->change_idx_size < 64 ?
379                    64 : kqop->change_idx_size * 2;
380                struct kqidx *new_change_idx;
381
382                while (new_size < fd)
383                        new_size *= 2;
384
385                new_change_idx = realloc(
386                        kqop->change_idx, new_size*sizeof(struct kqidx));
387                if (!new_change_idx)
388                        return NULL;
389                for (i = kqop->change_idx_size; i < new_size; ++i) {
390                        new_change_idx[i].read_idx = -1;
391                        new_change_idx[i].write_idx = -1;
392                }
393                kqop->change_idx = new_change_idx;
394                kqop->change_idx_size = new_size;
395        }
396        changes_ok(kqop);
397        return &kqop->change_idx[fd];
398}
399
400static int
401kq_add(void *arg, struct event *ev)
402{
403        struct kqop *kqop = arg;
404        struct kevent kev, *kev_old;
405        struct kqidx *kqidx;
406
407        changes_ok(kqop);
408        if (ev->ev_events & EV_SIGNAL) {
409                int nsignal = EVENT_SIGNAL(ev);
410
411                assert(nsignal >= 0 && nsignal < NSIG);
412                if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) {
413                        struct timespec timeout = { 0, 0 };
414                       
415                        memset(&kev, 0, sizeof(kev));
416                        kev.ident = nsignal;
417                        kev.filter = EVFILT_SIGNAL;
418                        kev.flags = EV_ADD;
419                        kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]);
420                       
421                        /* Be ready for the signal if it is sent any
422                         * time between now and the next call to
423                         * kq_dispatch. */
424                        if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
425                                return (-1);
426                       
427                        if (_evsignal_set_handler(ev->ev_base, nsignal,
428                                kq_sighandler) == -1)
429                                return (-1);
430                }
431
432                TAILQ_INSERT_TAIL(&kqop->evsigevents[nsignal], ev,
433                    ev_signal_next);
434                ev->ev_flags |= EVLIST_X_KQINKERNEL;
435                return (0);
436        }
437
438        if (ev->ev_fd < 0)
439                return (-1);
440
441        kqidx = kqidx_get_for_fd(kqop, ev->ev_fd);
442
443        if (ev->ev_events & EV_READ) {
444
445                if (kqidx->read_idx >= 0) {
446                        kev_old = &kqop->changes[kqidx->read_idx];
447                        assert(kev_old->ident == ev->ev_fd);
448                        assert(kev_old->filter == EVFILT_READ);
449
450                        if (kev_old->flags & EV_DELETE) {
451#ifdef NOTE_EOF
452                                /* Make it behave like select() and poll() */
453                                kev_old->fflags = NOTE_EOF;
454#endif
455                                kev_old->flags = EV_ADD;
456                                kev_old->udata = PTR_TO_UDATA(ev);
457                                if (!(ev->ev_events & EV_PERSIST))
458                                        kev_old->flags |= EV_ONESHOT;
459                        }
460                } else {
461
462                memset(&kev, 0, sizeof(kev));
463                kev.ident = ev->ev_fd;
464                kev.filter = EVFILT_READ;
465#ifdef NOTE_EOF
466                /* Make it behave like select() and poll() */
467                kev.fflags = NOTE_EOF;
468#endif
469                kev.flags = EV_ADD;
470                if (!(ev->ev_events & EV_PERSIST))
471                        kev.flags |= EV_ONESHOT;
472                kev.udata = PTR_TO_UDATA(ev);
473               
474                if (kq_insert(kqop, &kev) == -1)
475                        return (-1);
476                kqidx->read_idx = kqop->nchanges - 1;
477                }
478                ev->ev_flags |= EVLIST_X_KQINKERNEL;
479        }
480        changes_ok(kqop);
481
482        if (ev->ev_events & EV_WRITE) {
483                if (kqidx->write_idx >= 0) {
484                        kev_old = &kqop->changes[kqidx->write_idx];
485                        assert(kev_old->ident == ev->ev_fd);
486                        assert(kev_old->filter == EVFILT_WRITE);
487
488                        if (kev_old->flags & EV_DELETE) {
489                                kev_old->flags = EV_ADD;
490                                kev_old->udata = PTR_TO_UDATA(ev);
491                                if (!(ev->ev_events & EV_PERSIST))
492                                        kev_old->flags |= EV_ONESHOT;
493                        }
494                } else {
495
496                memset(&kev, 0, sizeof(kev));
497                kev.ident = ev->ev_fd;
498                kev.filter = EVFILT_WRITE;
499                kev.flags = EV_ADD;
500                if (!(ev->ev_events & EV_PERSIST))
501                        kev.flags |= EV_ONESHOT;
502                kev.udata = PTR_TO_UDATA(ev);
503               
504                if (kq_insert(kqop, &kev) == -1)
505                        return (-1);
506                kqidx->write_idx = kqop->nchanges - 1;
507                }
508                ev->ev_flags |= EVLIST_X_KQINKERNEL;
509        }
510        changes_ok(kqop);
511        return (0);
512}
513
514static int
515kq_del(void *arg, struct event *ev)
516{
517        struct kqop *kqop = arg;
518        struct kevent kev, *kev_old;
519        struct kqidx *kqidx;
520
521        changes_ok(kqop);
522        if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
523                return (0);
524
525        if (ev->ev_events & EV_SIGNAL) {
526                int nsignal = EVENT_SIGNAL(ev);
527                struct timespec timeout = { 0, 0 };
528
529                assert(nsignal >= 0 && nsignal < NSIG);
530                TAILQ_REMOVE(&kqop->evsigevents[nsignal], ev, ev_signal_next);
531                if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) {
532                        memset(&kev, 0, sizeof(kev));
533                        kev.ident = nsignal;
534                        kev.filter = EVFILT_SIGNAL;
535                        kev.flags = EV_DELETE;
536               
537                        /* Because we insert signal events
538                         * immediately, we need to delete them
539                         * immediately, too */
540                        if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
541                                return (-1);
542
543                        if (_evsignal_restore_handler(ev->ev_base,
544                                nsignal) == -1)
545                                return (-1);
546                }
547
548                ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
549                return (0);
550        }
551
552        if (ev->ev_fd < 0)
553                return -1;
554
555        kqidx = kqidx_get_for_fd(kqop, ev->ev_fd);
556
557        if (ev->ev_events & EV_READ) {
558                if (kqidx->read_idx >= 0) {
559                        kev_old = &kqop->changes[kqidx->read_idx];
560                        assert(kev_old->ident == ev->ev_fd);
561                        assert(kev_old->filter == EVFILT_READ);
562                        if (kev_old->flags & EV_ADD) {
563                                kev_old->flags = EV_DELETE;
564                        }
565                } else {
566
567                memset(&kev, 0, sizeof(kev));
568                kev.ident = ev->ev_fd;
569                kev.filter = EVFILT_READ;
570                kev.flags = EV_DELETE;
571               
572                if (kq_insert(kqop, &kev) == -1)
573                        return (-1);
574                kqidx->read_idx = kqop->nchanges - 1;
575                }
576                ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
577        }
578
579        changes_ok(kqop);
580
581        if (ev->ev_events & EV_WRITE) {
582                if (kqidx->write_idx >= 0) {
583                        kev_old = &kqop->changes[kqidx->write_idx];
584                        assert(kev_old->ident == ev->ev_fd);
585                        assert(kev_old->filter == EVFILT_WRITE);
586
587                        if (kev_old->flags & EV_ADD) {
588                                kev_old->flags = EV_DELETE;
589                        }
590                } else {
591                memset(&kev, 0, sizeof(kev));
592                kev.ident = ev->ev_fd;
593                kev.filter = EVFILT_WRITE;
594                kev.flags = EV_DELETE;
595               
596                if (kq_insert(kqop, &kev) == -1)
597                        return (-1);
598                kqidx->write_idx = kqop->nchanges - 1;
599                }
600                ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
601        }
602
603        changes_ok(kqop);
604        return (0);
605}
606
607static void
608kq_dealloc(struct event_base *base, void *arg)
609{
610        struct kqop *kqop = arg;
611
612        evsignal_dealloc(base);
613
614        if (kqop->changes)
615                free(kqop->changes);
616        if (kqop->events)
617                free(kqop->events);
618        if (kqop->kq >= 0 && kqop->pid == getpid())
619                close(kqop->kq);
620
621        memset(kqop, 0, sizeof(struct kqop));
622        free(kqop);
623}
Note: See TracBrowser for help on using the repository browser.