summaryrefslogtreecommitdiff
path: root/kqueue.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2011-06-08 14:56:19 -0400
committerNick Mathewson <nickm@torproject.org>2011-06-08 15:34:52 -0400
commit1fd34ab424b45f48dfeece108910978c79a8b7f6 (patch)
treeae9ef985b521c006d80b154bd97fd7d5c93afd8d /kqueue.c
parent09fe97da3b0dcdb6ee172ff8e4f710e0baad2d1c (diff)
downloadlibevent-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.c65
1 files changed, 46 insertions, 19 deletions
diff --git a/kqueue.c b/kqueue.c
index 8b486346..2e8b1724 100644
--- a/kqueue.c
+++ b/kqueue.c
@@ -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;