Annotation of embedaddon/libevent/kqueue.c, revision 1.1
1.1 ! misho 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: struct kqop {
! 73: struct kevent *changes;
! 74: int nchanges;
! 75: struct kevent *events;
! 76: struct event_list evsigevents[NSIG];
! 77: int nevents;
! 78: int kq;
! 79: pid_t pid;
! 80: };
! 81:
! 82: static void *kq_init (struct event_base *);
! 83: static int kq_add (void *, struct event *);
! 84: static int kq_del (void *, struct event *);
! 85: static int kq_dispatch (struct event_base *, void *, struct timeval *);
! 86: static int kq_insert (struct kqop *, struct kevent *);
! 87: static void kq_dealloc (struct event_base *, void *);
! 88:
! 89: const struct eventop kqops = {
! 90: "kqueue",
! 91: kq_init,
! 92: kq_add,
! 93: kq_del,
! 94: kq_dispatch,
! 95: kq_dealloc,
! 96: 1 /* need reinit */
! 97: };
! 98:
! 99: static void *
! 100: kq_init(struct event_base *base)
! 101: {
! 102: int i, kq;
! 103: struct kqop *kqueueop;
! 104:
! 105: /* Disable kqueue when this environment variable is set */
! 106: if (evutil_getenv("EVENT_NOKQUEUE"))
! 107: return (NULL);
! 108:
! 109: if (!(kqueueop = calloc(1, sizeof(struct kqop))))
! 110: return (NULL);
! 111:
! 112: /* Initalize the kernel queue */
! 113:
! 114: if ((kq = kqueue()) == -1) {
! 115: event_warn("kqueue");
! 116: free (kqueueop);
! 117: return (NULL);
! 118: }
! 119:
! 120: kqueueop->kq = kq;
! 121:
! 122: kqueueop->pid = getpid();
! 123:
! 124: /* Initalize fields */
! 125: kqueueop->changes = malloc(NEVENT * sizeof(struct kevent));
! 126: if (kqueueop->changes == NULL) {
! 127: free (kqueueop);
! 128: return (NULL);
! 129: }
! 130: kqueueop->events = malloc(NEVENT * sizeof(struct kevent));
! 131: if (kqueueop->events == NULL) {
! 132: free (kqueueop->changes);
! 133: free (kqueueop);
! 134: return (NULL);
! 135: }
! 136: kqueueop->nevents = NEVENT;
! 137:
! 138: /* we need to keep track of multiple events per signal */
! 139: for (i = 0; i < NSIG; ++i) {
! 140: TAILQ_INIT(&kqueueop->evsigevents[i]);
! 141: }
! 142:
! 143: /* Check for Mac OS X kqueue bug. */
! 144: memset(&kqueueop->changes[0], 0, sizeof kqueueop->changes[0]);
! 145: kqueueop->changes[0].ident = -1;
! 146: kqueueop->changes[0].filter = EVFILT_READ;
! 147: kqueueop->changes[0].flags = EV_ADD;
! 148: /*
! 149: * If kqueue works, then kevent will succeed, and it will
! 150: * stick an error in events[0]. If kqueue is broken, then
! 151: * kevent will fail.
! 152: */
! 153: if (kevent(kq,
! 154: kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 ||
! 155: kqueueop->events[0].ident != -1 ||
! 156: kqueueop->events[0].flags != EV_ERROR) {
! 157: event_warn("%s: detected broken kqueue; not using.", __func__);
! 158: free(kqueueop->changes);
! 159: free(kqueueop->events);
! 160: free(kqueueop);
! 161: close(kq);
! 162: return (NULL);
! 163: }
! 164:
! 165: return (kqueueop);
! 166: }
! 167:
! 168: static int
! 169: kq_insert(struct kqop *kqop, struct kevent *kev)
! 170: {
! 171: int nevents = kqop->nevents;
! 172:
! 173: if (kqop->nchanges == nevents) {
! 174: struct kevent *newchange;
! 175: struct kevent *newresult;
! 176:
! 177: nevents *= 2;
! 178:
! 179: newchange = realloc(kqop->changes,
! 180: nevents * sizeof(struct kevent));
! 181: if (newchange == NULL) {
! 182: event_warn("%s: malloc", __func__);
! 183: return (-1);
! 184: }
! 185: kqop->changes = newchange;
! 186:
! 187: newresult = realloc(kqop->events,
! 188: nevents * sizeof(struct kevent));
! 189:
! 190: /*
! 191: * If we fail, we don't have to worry about freeing,
! 192: * the next realloc will pick it up.
! 193: */
! 194: if (newresult == NULL) {
! 195: event_warn("%s: malloc", __func__);
! 196: return (-1);
! 197: }
! 198: kqop->events = newresult;
! 199:
! 200: kqop->nevents = nevents;
! 201: }
! 202:
! 203: memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
! 204:
! 205: event_debug(("%s: fd %d %s%s",
! 206: __func__, (int)kev->ident,
! 207: kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE",
! 208: kev->flags == EV_DELETE ? " (del)" : ""));
! 209:
! 210: return (0);
! 211: }
! 212:
! 213: static void
! 214: kq_sighandler(int sig)
! 215: {
! 216: /* Do nothing here */
! 217: }
! 218:
! 219: static int
! 220: kq_dispatch(struct event_base *base, void *arg, struct timeval *tv)
! 221: {
! 222: struct kqop *kqop = arg;
! 223: struct kevent *changes = kqop->changes;
! 224: struct kevent *events = kqop->events;
! 225: struct event *ev;
! 226: struct timespec ts, *ts_p = NULL;
! 227: int i, res;
! 228:
! 229: if (tv != NULL) {
! 230: TIMEVAL_TO_TIMESPEC(tv, &ts);
! 231: ts_p = &ts;
! 232: }
! 233:
! 234: res = kevent(kqop->kq, changes, kqop->nchanges,
! 235: events, kqop->nevents, ts_p);
! 236: kqop->nchanges = 0;
! 237: if (res == -1) {
! 238: if (errno != EINTR) {
! 239: event_warn("kevent");
! 240: return (-1);
! 241: }
! 242:
! 243: return (0);
! 244: }
! 245:
! 246: event_debug(("%s: kevent reports %d", __func__, res));
! 247:
! 248: for (i = 0; i < res; i++) {
! 249: int which = 0;
! 250:
! 251: if (events[i].flags & EV_ERROR) {
! 252: /*
! 253: * Error messages that can happen, when a delete fails.
! 254: * EBADF happens when the file discriptor has been
! 255: * closed,
! 256: * ENOENT when the file discriptor was closed and
! 257: * then reopened.
! 258: * EINVAL for some reasons not understood; EINVAL
! 259: * should not be returned ever; but FreeBSD does :-\
! 260: * An error is also indicated when a callback deletes
! 261: * an event we are still processing. In that case
! 262: * the data field is set to ENOENT.
! 263: */
! 264: if (events[i].data == EBADF ||
! 265: events[i].data == EINVAL ||
! 266: events[i].data == ENOENT)
! 267: continue;
! 268: errno = events[i].data;
! 269: return (-1);
! 270: }
! 271:
! 272: if (events[i].filter == EVFILT_READ) {
! 273: which |= EV_READ;
! 274: } else if (events[i].filter == EVFILT_WRITE) {
! 275: which |= EV_WRITE;
! 276: } else if (events[i].filter == EVFILT_SIGNAL) {
! 277: which |= EV_SIGNAL;
! 278: }
! 279:
! 280: if (!which)
! 281: continue;
! 282:
! 283: if (events[i].filter == EVFILT_SIGNAL) {
! 284: struct event_list *head =
! 285: (struct event_list *)events[i].udata;
! 286: TAILQ_FOREACH(ev, head, ev_signal_next) {
! 287: event_active(ev, which, events[i].data);
! 288: }
! 289: } else {
! 290: ev = (struct event *)events[i].udata;
! 291:
! 292: if (!(ev->ev_events & EV_PERSIST))
! 293: ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
! 294:
! 295: event_active(ev, which, 1);
! 296: }
! 297: }
! 298:
! 299: return (0);
! 300: }
! 301:
! 302:
! 303: static int
! 304: kq_add(void *arg, struct event *ev)
! 305: {
! 306: struct kqop *kqop = arg;
! 307: struct kevent kev;
! 308:
! 309: if (ev->ev_events & EV_SIGNAL) {
! 310: int nsignal = EVENT_SIGNAL(ev);
! 311:
! 312: assert(nsignal >= 0 && nsignal < NSIG);
! 313: if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) {
! 314: struct timespec timeout = { 0, 0 };
! 315:
! 316: memset(&kev, 0, sizeof(kev));
! 317: kev.ident = nsignal;
! 318: kev.filter = EVFILT_SIGNAL;
! 319: kev.flags = EV_ADD;
! 320: kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]);
! 321:
! 322: /* Be ready for the signal if it is sent any
! 323: * time between now and the next call to
! 324: * kq_dispatch. */
! 325: if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
! 326: return (-1);
! 327:
! 328: if (_evsignal_set_handler(ev->ev_base, nsignal,
! 329: kq_sighandler) == -1)
! 330: return (-1);
! 331: }
! 332:
! 333: TAILQ_INSERT_TAIL(&kqop->evsigevents[nsignal], ev,
! 334: ev_signal_next);
! 335: ev->ev_flags |= EVLIST_X_KQINKERNEL;
! 336: return (0);
! 337: }
! 338:
! 339: if (ev->ev_events & EV_READ) {
! 340: memset(&kev, 0, sizeof(kev));
! 341: kev.ident = ev->ev_fd;
! 342: kev.filter = EVFILT_READ;
! 343: #ifdef NOTE_EOF
! 344: /* Make it behave like select() and poll() */
! 345: kev.fflags = NOTE_EOF;
! 346: #endif
! 347: kev.flags = EV_ADD;
! 348: if (!(ev->ev_events & EV_PERSIST))
! 349: kev.flags |= EV_ONESHOT;
! 350: kev.udata = PTR_TO_UDATA(ev);
! 351:
! 352: if (kq_insert(kqop, &kev) == -1)
! 353: return (-1);
! 354:
! 355: ev->ev_flags |= EVLIST_X_KQINKERNEL;
! 356: }
! 357:
! 358: if (ev->ev_events & EV_WRITE) {
! 359: memset(&kev, 0, sizeof(kev));
! 360: kev.ident = ev->ev_fd;
! 361: kev.filter = EVFILT_WRITE;
! 362: kev.flags = EV_ADD;
! 363: if (!(ev->ev_events & EV_PERSIST))
! 364: kev.flags |= EV_ONESHOT;
! 365: kev.udata = PTR_TO_UDATA(ev);
! 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:
! 376: static int
! 377: kq_del(void *arg, struct event *ev)
! 378: {
! 379: struct kqop *kqop = arg;
! 380: struct kevent kev;
! 381:
! 382: if (!(ev->ev_flags & EVLIST_X_KQINKERNEL))
! 383: return (0);
! 384:
! 385: if (ev->ev_events & EV_SIGNAL) {
! 386: int nsignal = EVENT_SIGNAL(ev);
! 387: struct timespec timeout = { 0, 0 };
! 388:
! 389: assert(nsignal >= 0 && nsignal < NSIG);
! 390: TAILQ_REMOVE(&kqop->evsigevents[nsignal], ev, ev_signal_next);
! 391: if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) {
! 392: memset(&kev, 0, sizeof(kev));
! 393: kev.ident = nsignal;
! 394: kev.filter = EVFILT_SIGNAL;
! 395: kev.flags = EV_DELETE;
! 396:
! 397: /* Because we insert signal events
! 398: * immediately, we need to delete them
! 399: * immediately, too */
! 400: if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1)
! 401: return (-1);
! 402:
! 403: if (_evsignal_restore_handler(ev->ev_base,
! 404: nsignal) == -1)
! 405: return (-1);
! 406: }
! 407:
! 408: ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
! 409: return (0);
! 410: }
! 411:
! 412: if (ev->ev_events & EV_READ) {
! 413: memset(&kev, 0, sizeof(kev));
! 414: kev.ident = ev->ev_fd;
! 415: kev.filter = EVFILT_READ;
! 416: kev.flags = EV_DELETE;
! 417:
! 418: if (kq_insert(kqop, &kev) == -1)
! 419: return (-1);
! 420:
! 421: ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
! 422: }
! 423:
! 424: if (ev->ev_events & EV_WRITE) {
! 425: memset(&kev, 0, sizeof(kev));
! 426: kev.ident = ev->ev_fd;
! 427: kev.filter = EVFILT_WRITE;
! 428: kev.flags = EV_DELETE;
! 429:
! 430: if (kq_insert(kqop, &kev) == -1)
! 431: return (-1);
! 432:
! 433: ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
! 434: }
! 435:
! 436: return (0);
! 437: }
! 438:
! 439: static void
! 440: kq_dealloc(struct event_base *base, void *arg)
! 441: {
! 442: struct kqop *kqop = arg;
! 443:
! 444: evsignal_dealloc(base);
! 445:
! 446: if (kqop->changes)
! 447: free(kqop->changes);
! 448: if (kqop->events)
! 449: free(kqop->events);
! 450: if (kqop->kq >= 0 && kqop->pid == getpid())
! 451: close(kqop->kq);
! 452:
! 453: memset(kqop, 0, sizeof(struct kqop));
! 454: free(kqop);
! 455: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>