Index | Thread | Search

From:
Chris Waddey <chris@hoolizen.com>
To:
tech@openbsd.org
Date:
Thu, 02 Jan 2025 11:29:25 -0700

Download raw body.

Thread
  • Chris Waddey:

    (no subject)

Posting here from misc (with whitespace issue hopefully resolved).

Is there any desire for using kqueue natively in the wayland libraries
instead of epoll-shim? I'm sure epoll-shim is great, but I wanted to
learn to use kqueue and figured this was a good project (since I was
already working on a wlroots compositor for OpenBSD).

In any case, here's the diff. I've been running with this on my personal
latop for a couple of days now (unless I haven't linked properly), and I
haven't noticed any issues with it (I still have crashes, but those were
there before, and the core dumps don't indicate any issues

Thoughts? Should I try to get the wayland folks to merge this upstream?

diff --git a/meson.build b/meson.build
index 6b05bb5..0105188 100644
--- a/meson.build
+++ b/meson.build
@@ -18,6 +18,8 @@ config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
 cc_args = []
 if host_machine.system() not in ['freebsd', 'openbsd']
 	cc_args += ['-D_POSIX_C_SOURCE=200809L']
+else
+	cc_args += ['-D__USE_KQUEUE']
 endif
 add_project_arguments(cc_args, language: 'c')
 
@@ -72,18 +74,22 @@ if get_option('libraries')
 	if host_machine.system() in ['freebsd', 'openbsd']
 		# When building for FreeBSD, epoll(7) is provided by a userspace
 		# wrapper around kqueue(2).
-		epoll_dep = dependency('epoll-shim')
+		epoll_dep = []
+		decls = [
+		{ 'header': 'time.h', 'symbol': 'CLOCK_MONOTONIC' },
+		]
+
 	else
 		# Otherwise, assume that epoll(7) is supported natively.
 		epoll_dep = []
-	endif
-	ffi_dep = dependency('libffi')
-
-	decls = [
+		decls = [
 		{ 'header': 'sys/signalfd.h', 'symbol': 'SFD_CLOEXEC' },
 		{ 'header': 'sys/timerfd.h', 'symbol': 'TFD_CLOEXEC' },
 		{ 'header': 'time.h', 'symbol': 'CLOCK_MONOTONIC' },
-	]
+		]
+
+	endif
+	ffi_dep = dependency('libffi')
 
 	foreach d: decls
 		if not cc.has_header_symbol(d['header'], d['symbol'], dependencies: epoll_dep, args: cc_args)
diff --git a/src/event-loop.c b/src/event-loop.c
index 51c9b9d..59af9fd 100644
--- a/src/event-loop.c
+++ b/src/event-loop.c
@@ -34,9 +34,15 @@
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#ifdef __USE_KQUEUE
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#else
 #include <sys/epoll.h>
 #include <sys/signalfd.h>
 #include <sys/timerfd.h>
+#endif
 #include <unistd.h>
 #include "wayland-util.h"
 #include "wayland-private.h"
@@ -68,7 +74,11 @@ struct wl_timer_heap {
 };
 
 struct wl_event_loop {
+#ifdef __USE_KQUEUE
+	int kqueue_fd;
+#else
 	int epoll_fd;
+#endif
 	struct wl_list check_list;
 	struct wl_list idle_list;
 	struct wl_list destroy_list;
@@ -78,11 +88,82 @@ struct wl_event_loop {
 	struct wl_timer_heap timers;
 };
 
+#ifdef __USE_KQUEUE
+
+struct kevent_pair {
+#define KEPM_READ 0x1
+#define KEPM_WRITE 0x2
+	uint32_t	mask;
+	int		fd;
+	struct kevent	kqread;
+	struct kevent	kqwrite;
+};
+
 struct wl_event_source_interface {
 	int (*dispatch)(struct wl_event_source *source,
-			struct epoll_event *ep);
+			struct kevent_pair *kp);
 };
 
+static int
+kq_modify_helper(struct wl_event_source *source, uint32_t mask) {
+	struct wl_event_loop *loop = source->loop;
+	struct kevent karray[2];
+	int retval, tries = 0;
+
+again:
+	if (mask & WL_EVENT_READABLE)
+		EV_SET(&karray[0], source->fd, EVFILT_READ,
+		    EV_ADD|EV_RECEIPT, 0, 0, source);
+	else
+		EV_SET(&karray[0], source->fd, EVFILT_READ,
+		    EV_DELETE|EV_RECEIPT, 0, 0, source);
+	if (mask & WL_EVENT_WRITABLE)
+		EV_SET(&karray[1], source->fd, EVFILT_WRITE,
+		    EV_ADD|EV_RECEIPT, 0, 0, source);
+	else
+		EV_SET(&karray[1], source->fd, EVFILT_WRITE,
+		    EV_DELETE|EV_RECEIPT, 0, 0, source);
+
+	retval = kevent(loop->kqueue_fd, karray, 2, karray, 2, NULL);
+	/* The following if is probably very rarely going to be hit. */
+	if (retval != 2) {
+		wl_log("wl_event_source_fd_update retval != 2");
+		if (retval == -1 && errno == EINTR)
+			goto again;
+		if (retval == 1 && tries < 3) {
+			/* If only one went through, try again. */
+			tries++;
+			if (tries >= 3)
+				return -1;
+			goto again;
+		}
+		return -1;
+	}
+	retval = 0;
+	if (mask & WL_EVENT_READABLE) {
+		if (karray[0].data != 0) /* Didn't get it. */
+			retval = -1;
+	} else {
+		if (karray[0].data != 0 && karray[0].data != ENOENT)
+			retval = -1; /* Didn't remove it. */
+	}
+	if (mask & WL_EVENT_WRITABLE) {
+		if (karray[1].data != 0) /* Didn't get it. */
+			retval = -1;
+	} else {
+		if (karray[1].data != 0 && karray[1].data != ENOENT)
+			retval = -1; /* Didn't remove it. */
+	}
+
+	return retval;
+
+}
+#else
+struct wl_event_source_interface {
+	int (*dispatch)(struct wl_event_source *source,
+			struct epoll_event *ep);
+};
+#endif
 
 struct wl_event_source_fd {
 	struct wl_event_source base;
@@ -92,15 +173,37 @@ struct wl_event_source_fd {
 
 /** \endcond */
 
+#ifdef __USE_KQUEUE
+static int
+wl_event_source_fd_dispatch(struct wl_event_source *source,
+			    struct kevent_pair *kp)
+#else
 static int
 wl_event_source_fd_dispatch(struct wl_event_source *source,
 			    struct epoll_event *ep)
+#endif
 {
 	struct wl_event_source_fd *fd_source = (struct wl_event_source_fd *) source;
 	uint32_t mask;
 
 	mask = 0;
-	if (ep->events & EPOLLIN)
+#ifdef __USE_KQUEUE
+	if (kp->mask & KEPM_READ) {
+		mask |= WL_EVENT_READABLE;
+		if (kp->kqread.flags & EV_EOF)
+			mask |= WL_EVENT_HANGUP;
+		if (kp->kqread.flags & EV_ERROR)
+			mask |= WL_EVENT_ERROR;
+	}
+	if (kp->mask & KEPM_WRITE) {
+		mask |= WL_EVENT_WRITABLE;
+		if (kp->kqwrite.flags & EV_EOF)
+			mask |= WL_EVENT_HANGUP;
+		if (kp->kqwrite.flags & EV_ERROR)
+			mask |= WL_EVENT_ERROR;
+	}
+#else
+        if (ep->events & EPOLLIN)
 		mask |= WL_EVENT_READABLE;
 	if (ep->events & EPOLLOUT)
 		mask |= WL_EVENT_WRITABLE;
@@ -108,6 +211,7 @@ wl_event_source_fd_dispatch(struct wl_event_source *source,
 		mask |= WL_EVENT_HANGUP;
 	if (ep->events & EPOLLERR)
 		mask |= WL_EVENT_ERROR;
+#endif
 
 	return fd_source->func(fd_source->fd, mask, source->data);
 }
@@ -120,7 +224,12 @@ static struct wl_event_source *
 add_source(struct wl_event_loop *loop,
 	   struct wl_event_source *source, uint32_t mask, void *data)
 {
+#ifdef __USE_KQUEUE
+	struct kevent karray[2];
+	int retval;
+#else
 	struct epoll_event ep;
+#endif
 
 	if (source->fd < 0) {
 		free(source);
@@ -131,6 +240,20 @@ add_source(struct wl_event_loop *loop,
 	source->data = data;
 	wl_list_init(&source->link);
 
+#ifdef __USE_KQUEUE
+	retval = kq_modify_helper(source, mask);
+	if (retval != 0) {
+		/* If we fail remove everything. */
+		EV_SET(&karray[0], source->fd, EVFILT_READ,
+		    EV_DELETE, 0, 0, source);
+		EV_SET(&karray[1], source->fd, EVFILT_WRITE,
+		    EV_DELETE, 0, 0, source);
+		kevent(loop->kqueue_fd, karray, 2, 0, 0, NULL);
+		close(source->fd);
+		free(source);
+		return NULL;
+	}
+#else
 	memset(&ep, 0, sizeof ep);
 	if (mask & WL_EVENT_READABLE)
 		ep.events |= EPOLLIN;
@@ -143,6 +266,7 @@ add_source(struct wl_event_loop *loop,
 		free(source);
 		return NULL;
 	}
+#endif
 
 	return source;
 }
@@ -214,6 +338,9 @@ wl_event_loop_add_fd(struct wl_event_loop *loop,
 WL_EXPORT int
 wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask)
 {
+#ifdef __USE_KQUEUE
+	return kq_modify_helper(source, mask);
+#else
 	struct wl_event_loop *loop = source->loop;
 	struct epoll_event ep;
 
@@ -225,6 +352,7 @@ wl_event_source_fd_update(struct wl_event_source *source, uint32_t mask)
 	ep.data.ptr = source;
 
 	return epoll_ctl(loop->epoll_fd, EPOLL_CTL_MOD, source->fd, &ep);
+#endif
 }
 
 /** \cond INTERNAL */
@@ -237,11 +365,19 @@ struct wl_event_source_timer {
 	int heap_idx;
 };
 
+#ifdef __USE_KQUEUE
+static int
+noop_dispatch(struct wl_event_source *source,
+	      struct kevent_pair *ep) {
+	return 0;
+}
+#else
 static int
 noop_dispatch(struct wl_event_source *source,
 	      struct epoll_event *ep) {
 	return 0;
 }
+#endif
 
 struct wl_event_source_interface timer_heap_source_interface = {
 	noop_dispatch,
@@ -256,6 +392,28 @@ time_lt(struct timespec ta, struct timespec tb)
 	return ta.tv_nsec < tb.tv_nsec;
 }
 
+#ifdef __USE_KQUEUE
+static int
+set_timer(struct wl_timer_heap *timers, struct timespec deadline) {
+	struct kevent ke;
+	int retval;
+again:
+	EV_SET(&ke, timers->base.fd, EVFILT_TIMER, EV_ADD|EV_RECEIPT,
+	    NOTE_ABSTIME|NOTE_NSECONDS,
+	    deadline.tv_sec*1000*1000*1000 + deadline.tv_nsec, NULL);
+	retval = kevent(timers->base.loop->kqueue_fd, &ke, 1, &ke, 1, NULL);
+	if (retval == -1) {
+		if (errno == EINTR)
+			goto again;
+		else
+			return -1;
+	}
+	if (ke.data != 0)
+		return -1;
+
+	return 0;
+}
+#else
 static int
 set_timer(int timerfd, struct timespec deadline) {
 	struct itimerspec its;
@@ -265,9 +423,32 @@ set_timer(int timerfd, struct timespec deadline) {
 	its.it_value = deadline;
 	return timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &its, NULL);
 }
+#endif
+
+#ifdef __USE_KQUEUE
+static int
+clear_timer(struct wl_timer_heap *timers)
+{
+	struct kevent ke;
+	int retval;
+again:
+	EV_SET(&ke, timers->base.fd, EVFILT_TIMER, EV_DELETE|EV_RECEIPT, 0, 0,
+	    NULL);
+	retval = kevent(timers->base.loop->kqueue_fd, &ke, 1, &ke, 1, NULL);
+	if (retval == -1) {
+		if (errno == EINTR)
+			goto again;
+		else
+			return -1;
+	}
+	if (ke.data != 0 && ke.data != ENOENT)
+		return -1;
 
+	return 0;
+}
+#else
 static int
-clear_timer(int timerfd)
+clear_timer(struct wl_timer_heap *timers)
 {
 	struct itimerspec its;
 
@@ -277,6 +458,7 @@ clear_timer(int timerfd)
 	its.it_value.tv_nsec = 0;
 	return timerfd_settime(timerfd, 0, &its, NULL);
 }
+#endif
 
 static void
 wl_timer_heap_init(struct wl_timer_heap *timers, struct wl_event_loop *loop)
@@ -296,15 +478,25 @@ wl_timer_heap_init(struct wl_timer_heap *timers, struct wl_event_loop *loop)
 static void
 wl_timer_heap_release(struct wl_timer_heap *timers)
 {
+#ifdef __USE_KQUEUE
+	clear_timer(timers);
+#else
 	if (timers->base.fd != -1) {
 		close(timers->base.fd);
 	}
+#endif
 	free(timers->data);
 }
 
 static int
 wl_timer_heap_ensure_timerfd(struct wl_timer_heap *timers)
 {
+#ifdef __USE_KQUEUE
+	if (timers->base.fd != -1)
+		return 0;
+	timers->base.fd = 37421; /* Just a random identifier. */
+	return 0;
+#else
 	struct epoll_event ep;
 	int timer_fd;
 
@@ -328,6 +520,7 @@ wl_timer_heap_ensure_timerfd(struct wl_timer_heap *timers)
 
 	timers->base.fd = timer_fd;
 	return 0;
+#endif
 }
 
 static int
@@ -512,6 +705,15 @@ wl_timer_heap_dispatch(struct wl_timer_heap *timers)
 	if (list_tail)
 		list_tail->next_due = NULL;
 
+#ifdef __USE_KQUEUE
+	if (timers->active > 0) {
+		if (set_timer(timers, timers->data[0]->deadline) < 0)
+			return -1;
+	} else {
+		if (clear_timer(timers) < 0)
+			return -1;
+	}
+#else
 	if (timers->active > 0) {
 		if (set_timer(timers->base.fd, timers->data[0]->deadline) < 0)
 			return -1;
@@ -519,6 +721,7 @@ wl_timer_heap_dispatch(struct wl_timer_heap *timers)
 		if (clear_timer(timers->base.fd) < 0)
 			return -1;
 	}
+#endif
 
 	/* Execute precisely the functions for events before `now`, in order.
 	 * Because wl_event_loop_dispatch ignores return codes, do the same
@@ -531,9 +734,15 @@ wl_timer_heap_dispatch(struct wl_timer_heap *timers)
 	return 0;
 }
 
+#ifdef __USE_KQUEUE
+static int
+wl_event_source_timer_dispatch(struct wl_event_source *source,
+			       struct kevent_pair *ep)
+#else
 static int
 wl_event_source_timer_dispatch(struct wl_event_source *source,
 			       struct epoll_event *ep)
+#endif
 {
 	struct wl_event_source_timer *timer;
 
@@ -641,8 +850,13 @@ wl_event_source_timer_update(struct wl_event_source *source, int ms_delay)
 		if (tsource->heap_idx == 0) {
 			/* Only update the timerfd if the new deadline is
 			 * the earliest */
+#ifdef __USE_KQUEUE
+			if (set_timer(timers, deadline) < 0)
+				return -1;
+#else
 			if (set_timer(timers->base.fd, deadline) < 0)
 				return -1;
+#endif
 		}
 	} else {
 		if (tsource->heap_idx == -1)
@@ -652,8 +866,13 @@ wl_event_source_timer_update(struct wl_event_source *source, int ms_delay)
 		if (timers->active == 0) {
 			/* Only update the timerfd if this was the last
 			 * active timer */
+#ifdef __USE_KQUEUE
+			if (clear_timer(timers) < 0)
+				return -1;
+#else
 			if (clear_timer(timers->base.fd) < 0)
 				return -1;
+#endif
 		}
 	}
 
@@ -670,6 +889,18 @@ struct wl_event_source_signal {
 
 /** \endcond */
 
+#ifdef __USE_KQUEUE
+static int
+wl_event_source_signal_dispatch(struct wl_event_source *source,
+				struct kevent_pair *unused)
+{
+	struct wl_event_source_signal *signal_source =
+	    (struct wl_event_source_signal *)source;
+
+	return signal_source->func(signal_source->signal_number,
+	    signal_source->base.data);
+}
+#else
 static int
 wl_event_source_signal_dispatch(struct wl_event_source *source,
 				struct epoll_event *ep)
@@ -687,6 +918,7 @@ wl_event_source_signal_dispatch(struct wl_event_source *source,
 	return signal_source->func(signal_source->signal_number,
 				   signal_source->base.data);
 }
+#endif
 
 struct wl_event_source_interface signal_source_interface = {
 	wl_event_source_signal_dispatch,
@@ -717,6 +949,10 @@ wl_event_loop_add_signal(struct wl_event_loop *loop,
 			 wl_event_loop_signal_func_t func,
 			 void *data)
 {
+#ifdef __USE_KQUEUE
+	struct kevent ke;
+	int retval;
+#endif
 	struct wl_event_source_signal *source;
 	sigset_t mask;
 
@@ -725,16 +961,35 @@ wl_event_loop_add_signal(struct wl_event_loop *loop,
 		return NULL;
 
 	source->base.interface = &signal_source_interface;
+	source->base.loop = loop;
+	wl_list_init(&source->base.link);
 	source->signal_number = signal_number;
+	source->base.data = data;
 
 	sigemptyset(&mask);
 	sigaddset(&mask, signal_number);
+#ifdef __USE_KQUEUE
+	source->base.fd = signal_number;
+#else
 	source->base.fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
+#endif
 	sigprocmask(SIG_BLOCK, &mask, NULL);
 
 	source->func = func;
 
+#ifdef __USE_KQUEUE
+again:
+	EV_SET(&ke, source->signal_number, EVFILT_SIGNAL, EV_ADD, 0, 0, data);
+	retval = kevent(loop->kqueue_fd, &ke, 1, &ke, 1, NULL);
+	if (retval == -1 && errno == EINTR)
+		goto again;
+	if (ke.data != 0)
+		return NULL;
+
+	return &source->base;
+#else
 	return add_source(loop, &source->base, WL_EVENT_READABLE, data);
+#endif
 }
 
 /** \cond INTERNAL */
@@ -832,6 +1087,60 @@ wl_event_source_remove(struct wl_event_source *source)
 {
 	struct wl_event_loop *loop = source->loop;
 
+#ifdef __USE_KQUEUE
+	struct kevent kp[2];
+	int retval;
+
+	if (source->interface == &timer_source_interface &&
+	    source->fd != TIMER_REMOVED) {
+		/* Disarm the timer (and the loop's timerfd, if necessary),
+		 * before removing its space in the loop timer heap */
+		wl_event_source_timer_update(source, 0);
+		wl_timer_heap_unreserve(&loop->timers);
+		/* Set the fd field to to indicate that the timer should NOT
+		 * be dispatched in `wl_event_loop_dispatch` */
+		source->fd = TIMER_REMOVED;
+	}
+
+again:
+	if (source->interface == &fd_source_interface) {
+		EV_SET(&kp[0], source->fd, EVFILT_READ, EV_DELETE|EV_RECEIPT,
+		    0, 0, source);
+		EV_SET(&kp[1], source->fd, EVFILT_WRITE, EV_DELETE|EV_RECEIPT,
+		    0, 0, source);
+		retval = kevent(loop->kqueue_fd, kp, 2, kp, 2, NULL);
+		if ((retval == -1 && errno == EAGAIN) || retval == 1)
+			goto again;
+		if (!(kp[0].data == 0 || kp[0].data == ENOENT) ||
+		    !(kp[1].data == 0 || kp[1].data == ENOENT))
+			goto again; /* Can this happen? */
+		close(source->fd);
+	}
+	if (source->interface == &timer_source_interface){
+		EV_SET(&kp[0], source->fd, EVFILT_TIMER, EV_DELETE|EV_RECEIPT,
+		    0, 0, source);
+		retval = kevent(loop->kqueue_fd, kp, 1, kp, 1, NULL);
+		if (retval == -1 && errno == EAGAIN)
+			goto again;
+		if (kp[0].data != 0 && kp[0].data != ENOENT)
+			goto again; /* Can this happen? */
+	}
+	if (source->interface == &signal_source_interface) {
+		EV_SET(&kp[0], source->fd, EVFILT_SIGNAL, EV_DELETE|EV_RECEIPT,
+		    0, 0, source);
+		retval = kevent(loop->kqueue_fd, kp, 1, kp, 1, NULL);
+		if (retval == -1 && errno == EAGAIN)
+			goto again;
+
+		if (kp[0].data != 0 && kp[0].data != ENOENT)
+			goto again; /* Can this happen? */
+	}
+
+	wl_list_remove(&source->link);
+	wl_list_insert(&loop->destroy_list, &source->link);
+
+	return 0;
+#else
 	/* We need to explicitly remove the fd, since closing the fd
 	 * isn't enough in case we've dup'ed the fd. */
 	if (source->fd >= 0) {
@@ -855,6 +1164,7 @@ wl_event_source_remove(struct wl_event_source *source)
 	wl_list_insert(&loop->destroy_list, &source->link);
 
 	return 0;
+#endif
 }
 
 static void
@@ -891,11 +1201,19 @@ wl_event_loop_create(void)
 	if (loop == NULL)
 		return NULL;
 
+#ifdef __USE_KQUEUE
+	loop->kqueue_fd = kqueue();
+	if (loop->kqueue_fd < 0) {
+		free(loop);
+		return NULL;
+	}
+#else
 	loop->epoll_fd = wl_os_epoll_create_cloexec();
 	if (loop->epoll_fd < 0) {
 		free(loop);
 		return NULL;
 	}
+#endif
 	wl_list_init(&loop->check_list);
 	wl_list_init(&loop->idle_list);
 	wl_list_init(&loop->destroy_list);
@@ -926,19 +1244,28 @@ wl_event_loop_destroy(struct wl_event_loop *loop)
 	wl_priv_signal_final_emit(&loop->destroy_signal, loop);
 
 	wl_event_loop_process_destroy_list(loop);
+#ifdef __USE_KQUEUE
+	wl_timer_heap_release(&loop->timers);
+	close(loop->kqueue_fd);
+#else
 	wl_timer_heap_release(&loop->timers);
 	close(loop->epoll_fd);
+#endif
 	free(loop);
 }
 
 static bool
 post_dispatch_check(struct wl_event_loop *loop)
 {
+#ifdef __USE_KQUEUE
+	struct kevent_pair ep = { 0 };
+#else
 	struct epoll_event ep;
+	ep.events = 0;
+#endif
 	struct wl_event_source *source, *next;
 	bool needs_recheck = false;
 
-	ep.events = 0;
 	wl_list_for_each_safe(source, next, &loop->check_list, link) {
 		int dispatch_result;
 
@@ -1047,7 +1374,14 @@ timespec_sub(struct timespec a, struct timespec b)
 WL_EXPORT int
 wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
 {
+#ifdef __USE_KQUEUE
+	struct kevent ke[32];
+	struct kevent_pair kp;
+	struct timespec to;
+	int j;
+#else
 	struct epoll_event ep[32];
+#endif
 	struct wl_event_source *source;
 	int i, count;
 	bool has_timers = false;
@@ -1062,7 +1396,13 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
 	}
 
 	while (true) {
+#ifdef __USE_KQUEUE
+		to = ms_to_timespec(timeout);
+		count = kevent(loop->kqueue_fd, NULL, 0, ke, ARRAY_LENGTH(ke),
+		    use_timeout ? &to : NULL);
+#else
 		count = epoll_wait(loop->epoll_fd, ep, ARRAY_LENGTH(ep), timeout);
+#endif
 		if (count >= 0)
 			break; /* have events or timeout */
 		else if (count < 0 && errno != EINTR && errno != EAGAIN)
@@ -1083,11 +1423,18 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
 		return -1;
 
 	for (i = 0; i < count; i++) {
+#ifdef __USE_KQUEUE
+		if (ke[i].filter == EVFILT_TIMER) {
+			has_timers = true;
+			break;
+		}
+#else
 		source = ep[i].data.ptr;
 		if (source == &loop->timers.base) {
 			has_timers = true;
 			break;
 		}
+#endif
 	}
 
 	if (has_timers) {
@@ -1101,9 +1448,52 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
 	}
 
 	for (i = 0; i < count; i++) {
+#ifdef __USE_KQUEUE
+		kp.mask = 0;
+		if (ke[i].filter == EVFILT_TIMER ||
+		    ke[i].filter == EVFILT_SYSCOUNT)
+			continue;
+		if (ke[i].filter == EVFILT_SIGNAL) {
+			source = ke[i].udata;
+			source->interface->dispatch(source, NULL);
+			continue;
+		}
+		if (ke[i].filter == EVFILT_READ) {
+			kp.kqread = ke[i];
+			kp.mask |= KEPM_READ;
+		}
+		if (ke[i].filter == EVFILT_WRITE) {
+			kp.kqwrite = ke[i];
+			kp.mask |= KEPM_WRITE;
+		}
+		if (kp.mask == 0) {
+			wl_log("Unhandled filter");
+			continue;
+		}
+		for (j = i + 1; j < count; j++) {
+			if (ke[i].ident == ke[j].ident) {
+				if (ke[j].filter == EVFILT_READ) {
+					kp.kqread = ke[j];
+					kp.mask |= KEPM_READ;
+					ke[j].filter = EVFILT_SYSCOUNT;
+					break;
+				}
+				if (ke[j].filter == EVFILT_WRITE) {
+					kp.kqwrite = ke[j];
+					kp.mask |= KEPM_READ;
+					ke[j].filter = EVFILT_SYSCOUNT;
+					break;
+				}
+				/* We get to here if signals and fds match. */
+			}
+		}
+		source = ke[i].udata;
+		source->interface->dispatch(source, &kp);
+#else
 		source = ep[i].data.ptr;
 		if (source->fd != -1)
 			source->interface->dispatch(source, &ep[i]);
+#endif
 	}
 
 	wl_event_loop_process_destroy_list(loop);
@@ -1134,7 +1524,11 @@ wl_event_loop_dispatch(struct wl_event_loop *loop, int timeout)
 WL_EXPORT int
 wl_event_loop_get_fd(struct wl_event_loop *loop)
 {
+#ifdef __USE_KQUEUE
+	return loop->kqueue_fd;
+#else
 	return loop->epoll_fd;
+#endif
 }
 
 /** Register a destroy listener for an event loop context
diff --git a/src/wayland-os.c b/src/wayland-os.c
index f00ead4..4d559e2 100644
--- a/src/wayland-os.c
+++ b/src/wayland-os.c
@@ -33,7 +33,15 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <string.h>
+
+#ifdef __USE_KQUEUE
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#else
 #include <sys/epoll.h>
+#endif
+
 #include <sys/mman.h>
 #include <sys/un.h>
 #ifdef HAVE_SYS_UCRED_H
@@ -46,7 +54,9 @@
 int (*wl_fcntl)(int fildes, int cmd, ...) = fcntl;
 int (*wl_socket)(int domain, int type, int protocol) = socket;
 ssize_t (*wl_recvmsg)(int socket, struct msghdr *message, int flags) = recvmsg;
+#ifndef __USE_KQUEUE
 int (*wl_epoll_create1)(int flags) = epoll_create1;
+#endif
 
 static int
 set_cloexec_or_close(int fd)
@@ -198,6 +208,7 @@ wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags)
 	return recvmsg_cloexec_fallback(sockfd, msg, flags);
 }
 
+#ifndef __USE_KQUEUE
 int
 wl_os_epoll_create_cloexec(void)
 {
@@ -214,6 +225,7 @@ wl_os_epoll_create_cloexec(void)
 	fd = epoll_create(1);
 	return set_cloexec_or_close(fd);
 }
+#endif
 
 int
 wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
diff --git a/src/wayland-os.h b/src/wayland-os.h
index 068fd2f..c672d82 100644
--- a/src/wayland-os.h
+++ b/src/wayland-os.h
@@ -41,8 +41,10 @@ wl_os_dupfd_cloexec(int fd, int minfd);
 ssize_t
 wl_os_recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags);
 
+#ifndef __USE_KQUEUE
 int
 wl_os_epoll_create_cloexec(void);
+#endif
 
 int
 wl_os_accept_cloexec(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 1d6be3e..d521b91 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -23,6 +23,7 @@
  * SOFTWARE.
  */
 
+#include "wayland-server-core.h"
 #define _GNU_SOURCE
 
 #include <stdbool.h>
@@ -39,7 +40,9 @@
 #include <dlfcn.h>
 #include <sys/time.h>
 #include <fcntl.h>
+#ifndef __USE_KQUEUE
 #include <sys/eventfd.h>
+#endif
 #include <sys/file.h>
 #include <sys/stat.h>
 
@@ -110,6 +113,9 @@ struct wl_display {
 	void *global_filter_data;
 
 	int terminate_efd;
+#ifdef __USE_KQUEUE
+	int terminate_efd_read;
+#endif
 	struct wl_event_source *term_source;
 
 	size_t max_buffer_size;
@@ -1155,6 +1161,10 @@ wl_display_create(void)
 	struct wl_display *display;
 	const char *debug;
 
+#ifdef __USE_KQUEUE
+	int termpipe[2];
+#endif
+
 	debug = getenv("WAYLAND_DEBUG");
 	if (debug && (strstr(debug, "server") || strstr(debug, "1")))
 		debug_server = 1;
@@ -1169,6 +1179,14 @@ wl_display_create(void)
 		return NULL;
 	}
 
+#ifdef __USE_KQUEUE
+	if (pipe2(termpipe, O_CLOEXEC|O_NONBLOCK) == -1)
+		goto err_eventfd;
+	display->terminate_efd_read = termpipe[0];
+	display->term_source = wl_event_loop_add_fd(display->loop, termpipe[0],
+	    WL_EVENT_READABLE, handle_display_terminate, NULL);
+	display->terminate_efd = termpipe[1];
+#else
 	display->terminate_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
 	if (display->terminate_efd < 0)
 		goto err_eventfd;
@@ -1178,7 +1196,7 @@ wl_display_create(void)
 						    WL_EVENT_READABLE,
 						    handle_display_terminate,
 						    NULL);
-
+#endif
 	if (display->term_source == NULL)
 		goto err_term_source;
 
@@ -1204,6 +1222,9 @@ wl_display_create(void)
 
 err_term_source:
 	close(display->terminate_efd);
+#ifdef __USE_KQUEUE
+	close(display->terminate_efd_read);
+#endif
 err_eventfd:
 	wl_event_loop_destroy(display->loop);
 	free(display);
@@ -1268,6 +1289,9 @@ wl_display_destroy(struct wl_display *display)
 	}
 
 	close(display->terminate_efd);
+#ifdef __USE_KQUEUE
+	close(display->terminate_efd_read);
+#endif
 	wl_event_source_remove(display->term_source);
 
 	wl_event_loop_destroy(display->loop);