]> arthur.barton.de Git - netatalk.git/blobdiff - libevent/kqueue.c
Merge pull request #17 from hat001/staticlibevent
[netatalk.git] / libevent / kqueue.c
index 920129826f46a6dbe009c3c6d095f8e564e69bee..51984a4e41d373f94886f2f755179278a706424f 100644 (file)
@@ -2,7 +2,7 @@
 
 /*
  * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
- * Copyright 2007-2010 Niels Provos and Nick Mathewson
+ * Copyright 2007-2012 Niels Provos and Nick Mathewson
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * easy way to tell them apart via autoconf, so we need to use OS macros. */
 #if defined(_EVENT_HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__)
 #define PTR_TO_UDATA(x)        ((intptr_t)(x))
+#define INT_TO_UDATA(x) ((intptr_t)(x))
 #else
 #define PTR_TO_UDATA(x)        (x)
+#define INT_TO_UDATA(x) ((void*)(x))
 #endif
 
 #include "event-internal.h"
@@ -169,15 +171,20 @@ kq_sighandler(int sig)
        /* Do nothing here */
 }
 
+#define ADD_UDATA 0x30303
+
 static void
 kq_setup_kevent(struct kevent *out, evutil_socket_t fd, int filter, short change)
 {
-       memset(out, 0, sizeof(out));
+       memset(out, 0, sizeof(struct kevent));
        out->ident = fd;
        out->filter = filter;
 
        if (change & EV_CHANGE_ADD) {
                out->flags = EV_ADD;
+               /* We set a magic number here so that we can tell 'add'
+                * errors from 'del' errors. */
+               out->udata = INT_TO_UDATA(ADD_UDATA);
                if (change & EV_ET)
                        out->flags |= EV_CLEAR;
 #ifdef NOTE_EOF
@@ -228,6 +235,23 @@ kq_build_changes_list(const struct event_changelist *changelist,
        return n_changes;
 }
 
+static int
+kq_grow_events(struct kqop *kqop, size_t new_size)
+{
+       struct kevent *newresult;
+
+       newresult = mm_realloc(kqop->events,
+           new_size * sizeof(struct kevent));
+
+       if (newresult) {
+               kqop->events = newresult;
+               kqop->events_size = new_size;
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
 static int
 kq_dispatch(struct event_base *base, struct timeval *tv)
 {
@@ -255,6 +279,25 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
        changes = kqop->changes;
        kqop->changes = NULL;
 
+       /* Make sure that 'events' is at least as long as the list of changes:
+        * otherwise errors in the changes can get reported as a -1 return
+        * value from kevent() rather than as EV_ERROR events in the events
+        * array.
+        *
+        * (We could instead handle -1 return values from kevent() by
+        * retrying with a smaller changes array or a larger events array,
+        * but this approach seems less risky for now.)
+        */
+       if (kqop->events_size < n_changes) {
+               int new_size = kqop->events_size;
+               do {
+                       new_size *= 2;
+               } while (new_size < n_changes);
+
+               kq_grow_events(kqop, new_size);
+               events = kqop->events;
+       }
+
        EVBASE_RELEASE_LOCK(base, th_base_lock);
 
        res = kevent(kqop->kq, changes, n_changes,
@@ -280,27 +323,52 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
                int which = 0;
 
                if (events[i].flags & EV_ERROR) {
-                       /*
-                        * Error messages that can happen, when a delete fails.
-                        *   EBADF happens when the file descriptor has been
-                        *   closed,
-                        *   ENOENT when the file descriptor was closed and
-                        *   then reopened.
-                        *   EINVAL for some reasons not understood; EINVAL
-                        *   should not be returned ever; but FreeBSD does :-\
-                        * An error is also indicated when a callback deletes
-                        * an event we are still processing.  In that case
-                        * the data field is set to ENOENT.
-                        */
-                       if (events[i].data == EBADF ||
-                           events[i].data == EINVAL ||
-                           events[i].data == ENOENT)
+                       switch (events[i].data) {
+
+                       /* Can occur on delete if we are not currently
+                        * watching any events on this fd.  That can
+                        * happen when the fd was closed and another
+                        * file was opened with that fd. */
+                       case ENOENT:
+                       /* Can occur for reasons not fully understood
+                        * on FreeBSD. */
+                       case EINVAL:
                                continue;
-                       errno = events[i].data;
-                       return (-1);
-               }
 
-               if (events[i].filter == EVFILT_READ) {
+                       /* Can occur on a delete if the fd is closed. */
+                       case EBADF:
+                               /* XXXX On NetBSD, we can also get EBADF if we
+                                * try to add the write side of a pipe, but
+                                * the read side has already been closed.
+                                * Other BSDs call this situation 'EPIPE'. It
+                                * would be good if we had a way to report
+                                * this situation. */
+                               continue;
+                       /* These two can occur on an add if the fd was one side
+                        * of a pipe, and the other side was closed. */
+                       case EPERM:
+                       case EPIPE:
+                               /* Report read events, if we're listening for
+                                * them, so that the user can learn about any
+                                * add errors.  (If the operation was a
+                                * delete, then udata should be cleared.) */
+                               if (events[i].udata) {
+                                       /* The operation was an add:
+                                        * report the error as a read. */
+                                       which |= EV_READ;
+                                       break;
+                               } else {
+                                       /* The operation was a del:
+                                        * report nothing. */
+                                       continue;
+                               }
+
+                       /* Other errors shouldn't occur. */
+                       default:
+                               errno = events[i].data;
+                               return (-1);
+                       }
+               } else if (events[i].filter == EVFILT_READ) {
                        which |= EV_READ;
                } else if (events[i].filter == EVFILT_WRITE) {
                        which |= EV_WRITE;
@@ -319,17 +387,9 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
        }
 
        if (res == kqop->events_size) {
-               struct kevent *newresult;
-               int size = kqop->events_size;
                /* We used all the events space that we have. Maybe we should
                   make it bigger. */
-               size *= 2;
-               newresult = mm_realloc(kqop->events,
-                   size * sizeof(struct kevent));
-               if (newresult) {
-                       kqop->events = newresult;
-                       kqop->events_size = size;
-               }
+               kq_grow_events(kqop, kqop->events_size * 2);
        }
 
        return (0);