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

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

(trunk) #2416 "libevent + libcurl + OS X == crash" -- remove our old libevent mirror, and instead use a snapshot from git://github.com/nmathewson/Libevent.git's "kqueue_crash_fix_1.4" branch from 2001-01-14 <http://github.com/nmathewson/Libevent/commits/kqueue_crash_fix_1.4>

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