]> granicus.if.org Git - libevent/commitdiff
Report kqueue ebadf, epipe, and eperm as EV_READ events
authorNick Mathewson <nickm@torproject.org>
Wed, 8 Jun 2011 18:56:19 +0000 (14:56 -0400)
committerNick Mathewson <nickm@torproject.org>
Wed, 8 Jun 2011 19:34:52 +0000 (15:34 -0400)
When asked to add one side of a pipe, and the other side has been
closed, kqueue on NetBSD will say EBADF; kqueue on FreeBSD will say
EPIPE, and kqueue on OpenBSD will say EPERM.  So treat all of these
as EV_READ events, to give the user an opportunity to notice that
the pipe is closed.

Diagnosed by Nicholas Marriott and Dale Rahn; based on a patch by
Nicholas Marriott.

kqueue.c

index 8b4863465ad2e10be4843944780702053bc2f0d5..2e8b17246ba107f2fc963c3c3e7b2b5a5db32f8a 100644 (file)
--- a/kqueue.c
+++ b/kqueue.c
  * 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,6 +171,8 @@ 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)
 {
@@ -178,6 +182,9 @@ kq_setup_kevent(struct kevent *out, evutil_socket_t fd, int filter, short change
 
        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
@@ -316,27 +323,47 @@ 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.  Can
+                        * occur on an add if the fd was one side of a pipe,
+                        * and the other side was closed. */
+                       case EBADF:
+                       /* 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;