X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libevent%2Fkqueue.c;h=51984a4e41d373f94886f2f755179278a706424f;hb=d2968df026cd9971caeab102f9348152b08b9132;hp=920129826f46a6dbe009c3c6d095f8e564e69bee;hpb=b0bcb8f6b0571592a50ce039882c9319e012a270;p=netatalk.git diff --git a/libevent/kqueue.c b/libevent/kqueue.c index 92012982..51984a4e 100644 --- a/libevent/kqueue.c +++ b/libevent/kqueue.c @@ -2,7 +2,7 @@ /* * Copyright 2000-2007 Niels Provos - * 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 @@ -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,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);