Skip to content

Commit 53a07fe

Browse files
committed
Replace pipe-based notification with EVFILT_USER where possible
Sufficiently recent kqueue implementations have an EVFILT_USER filter that we can use to wake up an event base from another thread. When it's supported, we now use this mechanism rather than our old (pipe-based) mechanism. This should save some time and complications on newer OSX and freebsds.
1 parent 9bf866f commit 53a07fe

File tree

4 files changed

+133
-2
lines changed

4 files changed

+133
-2
lines changed

Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,8 @@ noinst_HEADERS = util-internal.h mm-internal.h ipv6-internal.h \
234234
changelist-internal.h iocp-internal.h \
235235
ratelim-internal.h \
236236
evconfig-private.h \
237+
ratelim-internal.h \
238+
kqueue-internal.h \
237239
WIN32-Code/event2/event-config.h \
238240
WIN32-Code/evconfig-private.h \
239241
WIN32-Code/tree.h \

event.c

+19-2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
#include "ht-internal.h"
7373
#include "util-internal.h"
7474

75+
76+
#ifdef EVENT__HAVE_WORKING_KQUEUE
77+
#include "kqueue-internal.h"
78+
#endif
79+
7580
#ifdef EVENT__HAVE_EVENT_PORTS
7681
extern const struct eventop evportops;
7782
#endif
@@ -927,16 +932,18 @@ event_reinit(struct event_base *base)
927932
had_signal_added = 1;
928933
base->sig.ev_signal_added = 0;
929934
}
930-
if (base->th_notify_fd[0] != -1) {
935+
if (base->th_notify_fn != NULL) {
931936
was_notifiable = 1;
937+
base->th_notify_fn = NULL;
938+
}
939+
if (base->th_notify_fd[0] != -1) {
932940
event_del(&base->th_notify);
933941
EVUTIL_CLOSESOCKET(base->th_notify_fd[0]);
934942
if (base->th_notify_fd[1] != -1)
935943
EVUTIL_CLOSESOCKET(base->th_notify_fd[1]);
936944
base->th_notify_fd[0] = -1;
937945
base->th_notify_fd[1] = -1;
938946
event_debug_unassign(&base->th_notify);
939-
base->th_notify_fn = NULL;
940947
}
941948

942949
/* Replace the original evsel. */
@@ -3056,6 +3063,16 @@ evthread_make_base_notifiable(struct event_base *base)
30563063
return 0;
30573064
}
30583065

3066+
3067+
#if defined(EVENT__HAVE_WORKING_KQUEUE)
3068+
if (base->evsel == &kqops && event_kq_add_notify_event_(base) == 0) {
3069+
base->th_notify_fn = event_kq_notify_base_;
3070+
/* No need to add an event here; the backend can wake
3071+
* itself up just fine. */
3072+
return 0;
3073+
}
3074+
#endif
3075+
30593076
#ifdef EVENT__HAVE_EVENTFD
30603077
base->th_notify_fd[0] = evutil_eventfd_(0,
30613078
EVUTIL_EFD_CLOEXEC|EVUTIL_EFD_NONBLOCK);

kqueue-internal.h

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2012 Niels Provos and Nick Mathewson
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. The name of the author may not be used to endorse or promote products
13+
* derived from this software without specific prior written permission.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
#ifndef KQUEUE_INTERNAL_H_INCLUDED_
27+
#define KQUEUE_INTERNAL_H_INCLUDED_
28+
29+
/** Notification function, used to tell an event base to wake up from another
30+
* thread. Only works when event_kq_add_notify_event_() has previously been
31+
* called successfully on that base. */
32+
int event_kq_notify_base_(struct event_base *base);
33+
34+
/** Prepare a kqueue-using event base to receive notifications via an internal
35+
* EVFILT_USER event. Return 0 on sucess, -1 on failure.
36+
*/
37+
int event_kq_add_notify_event_(struct event_base *base);
38+
39+
#endif

kqueue.c

+73
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
#include "evthread-internal.h"
6666
#include "changelist-internal.h"
6767

68+
#include "kqueue-internal.h"
69+
6870
#define NEVENT 64
6971

7072
struct kqop {
@@ -74,6 +76,7 @@ struct kqop {
7476
struct kevent *events;
7577
int events_size;
7678
int kq;
79+
int notify_event_added;
7780
pid_t pid;
7881
};
7982

@@ -369,6 +372,10 @@ kq_dispatch(struct event_base *base, struct timeval *tv)
369372
which |= EV_WRITE;
370373
} else if (events[i].filter == EVFILT_SIGNAL) {
371374
which |= EV_SIGNAL;
375+
#ifdef EVFILT_USER
376+
} else if (events[i].filter == EVFILT_USER) {
377+
base->is_notify_pending = 0;
378+
#endif
372379
}
373380

374381
if (!which)
@@ -473,4 +480,70 @@ kq_sig_del(struct event_base *base, int nsignal, short old, short events, void *
473480
return (0);
474481
}
475482

483+
484+
/* OSX 10.6 and FreeBSD 8.1 add support for EVFILT_USER, which we can use
485+
* to wake up the event loop from another thread. */
486+
487+
/* Magic number we use for our filter ID. */
488+
#define NOTIFY_IDENT 42
489+
490+
int
491+
event_kq_add_notify_event_(struct event_base *base)
492+
{
493+
struct kqop *kqop = base->evbase;
494+
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
495+
struct kevent kev;
496+
struct timespec timeout = { 0, 0 };
497+
#endif
498+
499+
if (kqop->notify_event_added)
500+
return 0;
501+
502+
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
503+
memset(&kev, 0, sizeof(kev));
504+
kev.ident = NOTIFY_IDENT;
505+
kev.filter = EVFILT_USER;
506+
kev.flags = EV_ADD | EV_CLEAR;
507+
508+
if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) {
509+
event_warn("kevent: adding EVFILT_USER event");
510+
return -1;
511+
}
512+
513+
kqop->notify_event_added = 1;
514+
515+
return 0;
516+
#else
517+
return -1;
518+
#endif
519+
}
520+
521+
int
522+
event_kq_notify_base_(struct event_base *base)
523+
{
524+
struct kqop *kqop = base->evbase;
525+
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
526+
struct kevent kev;
527+
struct timespec timeout = { 0, 0 };
528+
#endif
529+
if (! kqop->notify_event_added)
530+
return -1;
531+
532+
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
533+
memset(&kev, 0, sizeof(kev));
534+
kev.ident = NOTIFY_IDENT;
535+
kev.filter = EVFILT_USER;
536+
kev.fflags = NOTE_TRIGGER;
537+
538+
if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) {
539+
event_warn("kevent: triggering EVFILT_USER event");
540+
return -1;
541+
}
542+
543+
return 0;
544+
#else
545+
return -1;
546+
#endif
547+
}
548+
476549
#endif /* EVENT__HAVE_KQUEUE */

0 commit comments

Comments
 (0)