Revert "Revert "Upgrade to 2.1.8-stable (2017-01-22)." and "Probably Mac build fix?"" This reverts commit 83a0c9c65a60a92d3ea5542596b3ba56db492c37. Bug: 64543673 Test: make checkbuild Test: Manual tombstoned test Change-Id: I84bb128d1dec433195f2cbdbf70236ba17fa9955
diff --git a/kqueue.c b/kqueue.c index 51984a4..a959c58 100644 --- a/kqueue.c +++ b/kqueue.c
@@ -27,11 +27,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "event2/event-config.h" +#include "evconfig-private.h" -#define _GNU_SOURCE +#ifdef EVENT__HAVE_KQUEUE #include <sys/types.h> -#ifdef _EVENT_HAVE_SYS_TIME_H +#ifdef EVENT__HAVE_SYS_TIME_H #include <sys/time.h> #endif #include <sys/queue.h> @@ -42,14 +43,14 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#ifdef _EVENT_HAVE_INTTYPES_H +#ifdef EVENT__HAVE_INTTYPES_H #include <inttypes.h> #endif /* Some platforms apparently define the udata field of struct kevent as * intptr_t, whereas others define it as void*. There doesn't seem to be an * 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__) +#if defined(EVENT__HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) && !defined(__CloudABI__) #define PTR_TO_UDATA(x) ((intptr_t)(x)) #define INT_TO_UDATA(x) ((intptr_t)(x)) #else @@ -64,6 +65,8 @@ #include "evthread-internal.h" #include "changelist-internal.h" +#include "kqueue-internal.h" + #define NEVENT 64 struct kqop { @@ -73,6 +76,7 @@ struct kevent *events; int events_size; int kq; + int notify_event_added; pid_t pid; }; @@ -87,8 +91,8 @@ const struct eventop kqops = { "kqueue", kq_init, - event_changelist_add, - event_changelist_del, + event_changelist_add_, + event_changelist_del_, kq_dispatch, kq_dealloc, 1 /* need reinit */, @@ -150,7 +154,7 @@ if (kevent(kq, kqueueop->changes, 1, kqueueop->events, NEVENT, NULL) != 1 || (int)kqueueop->events[0].ident != -1 || - kqueueop->events[0].flags != EV_ERROR) { + !(kqueueop->events[0].flags & EV_ERROR)) { event_warn("%s: detected broken kqueue; not using.", __func__); goto err; } @@ -165,12 +169,6 @@ return (NULL); } -static void -kq_sighandler(int sig) -{ - /* Do nothing here */ -} - #define ADD_UDATA 0x30303 static void @@ -262,7 +260,8 @@ int i, n_changes, res; if (tv != NULL) { - TIMEVAL_TO_TIMESPEC(tv, &ts); + ts.tv_sec = tv->tv_sec; + ts.tv_nsec = tv->tv_usec * 1000; ts_p = &ts; } @@ -272,7 +271,7 @@ if (n_changes < 0) return -1; - event_changelist_remove_all(&base->changelist, base); + event_changelist_remove_all_(&base->changelist, base); /* steal the changes array in case some broken code tries to call * dispatch twice at once. */ @@ -334,6 +333,23 @@ * on FreeBSD. */ case EINVAL: continue; +#if defined(__FreeBSD__) + /* + * This currently occurs if an FD is closed + * before the EV_DELETE makes it out via kevent(). + * The FreeBSD capabilities code sees the blank + * capability set and rejects the request to + * modify an event. + * + * To be strictly correct - when an FD is closed, + * all the registered events are also removed. + * Queuing EV_DELETE to a closed FD is wrong. + * The event(s) should just be deleted from + * the pending changelist. + */ + case ENOTCAPABLE: + continue; +#endif /* Can occur on a delete if the fd is closed. */ case EBADF: @@ -374,15 +390,19 @@ which |= EV_WRITE; } else if (events[i].filter == EVFILT_SIGNAL) { which |= EV_SIGNAL; +#ifdef EVFILT_USER + } else if (events[i].filter == EVFILT_USER) { + base->is_notify_pending = 0; +#endif } if (!which) continue; if (events[i].filter == EVFILT_SIGNAL) { - evmap_signal_active(base, events[i].ident, 1); + evmap_signal_active_(base, events[i].ident, 1); } else { - evmap_io_active(base, events[i].ident, which | EV_ET); + evmap_io_active_(base, events[i].ident, which | EV_ET); } } @@ -412,7 +432,7 @@ kq_dealloc(struct event_base *base) { struct kqop *kqop = base->evbase; - evsig_dealloc(base); + evsig_dealloc_(base); kqop_free(kqop); } @@ -438,9 +458,13 @@ if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) return (-1); - /* XXXX The manpage suggest we could use SIG_IGN instead of a - * do-nothing handler */ - if (_evsig_set_handler(base, nsignal, kq_sighandler) == -1) + /* We can set the handler for most signals to SIG_IGN and + * still have them reported to us in the queue. However, + * if the handler for SIGCHLD is SIG_IGN, the system reaps + * zombie processes for us, and we don't get any notification. + * This appears to be the only signal with this quirk. */ + if (evsig_set_handler_(base, nsignal, + nsignal == SIGCHLD ? SIG_DFL : SIG_IGN) == -1) return (-1); return (0); @@ -468,8 +492,76 @@ if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) return (-1); - if (_evsig_restore_handler(base, nsignal) == -1) + if (evsig_restore_handler_(base, nsignal) == -1) return (-1); return (0); } + + +/* OSX 10.6 and FreeBSD 8.1 add support for EVFILT_USER, which we can use + * to wake up the event loop from another thread. */ + +/* Magic number we use for our filter ID. */ +#define NOTIFY_IDENT 42 + +int +event_kq_add_notify_event_(struct event_base *base) +{ + struct kqop *kqop = base->evbase; +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + struct kevent kev; + struct timespec timeout = { 0, 0 }; +#endif + + if (kqop->notify_event_added) + return 0; + +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + memset(&kev, 0, sizeof(kev)); + kev.ident = NOTIFY_IDENT; + kev.filter = EVFILT_USER; + kev.flags = EV_ADD | EV_CLEAR; + + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) { + event_warn("kevent: adding EVFILT_USER event"); + return -1; + } + + kqop->notify_event_added = 1; + + return 0; +#else + return -1; +#endif +} + +int +event_kq_notify_base_(struct event_base *base) +{ + struct kqop *kqop = base->evbase; +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + struct kevent kev; + struct timespec timeout = { 0, 0 }; +#endif + if (! kqop->notify_event_added) + return -1; + +#if defined(EVFILT_USER) && defined(NOTE_TRIGGER) + memset(&kev, 0, sizeof(kev)); + kev.ident = NOTIFY_IDENT; + kev.filter = EVFILT_USER; + kev.fflags = NOTE_TRIGGER; + + if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) { + event_warn("kevent: triggering EVFILT_USER event"); + return -1; + } + + return 0; +#else + return -1; +#endif +} + +#endif /* EVENT__HAVE_KQUEUE */