diff options
author | Nick Mathewson <nickm@torproject.org> | 2011-06-08 14:56:19 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2011-06-08 15:34:52 -0400 |
commit | 1fd34ab424b45f48dfeece108910978c79a8b7f6 (patch) | |
tree | ae9ef985b521c006d80b154bd97fd7d5c93afd8d /kqueue.c | |
parent | 09fe97da3b0dcdb6ee172ff8e4f710e0baad2d1c (diff) | |
download | libevent-1fd34ab424b45f48dfeece108910978c79a8b7f6.tar.gz |
Report kqueue ebadf, epipe, and eperm as EV_READ events
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.
Diffstat (limited to 'kqueue.c')
-rw-r--r-- | kqueue.c | 65 |
1 files changed, 46 insertions, 19 deletions
@@ -51,8 +51,10 @@ * 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; |