From e7448676cca05ff700d6a7e4d503efce3091df37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Dokumac=C4=B1?= Date: Tue, 8 Sep 2026 20:06:28 +0200 Subject: [PATCH] Fix lost cross-thread wakeups on kqueue backends Register the EVFILT_USER async filter persistently (EV_ADD|EV_CLEAR) in us_internal_async_set and trigger it with a bare NOTE_TRIGGER in us_internal_async_wakeup. The previous per-wakeup EV_ADD|EV_ONESHOT never fires in an idle loop on FreeBSD: the trigger is silently lost, and the loop only iterates on unrelated later activity, delaying cross-thread wakeups. Epoll path untouched. --- src/eventing/epoll_kqueue.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/eventing/epoll_kqueue.c b/src/eventing/epoll_kqueue.c index 72c6118f..f54aa931 100644 --- a/src/eventing/epoll_kqueue.c +++ b/src/eventing/epoll_kqueue.c @@ -430,7 +430,6 @@ struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int f void us_internal_async_close(struct us_internal_async *a) { struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; - /* Note: This will fail most of the time as there probably is no pending trigger */ struct kevent event; EV_SET(&event, (uintptr_t) internal_cb, EVFILT_USER, EV_DELETE, 0, 0, internal_cb); kevent(internal_cb->loop->fd, &event, 1, NULL, 0, NULL); @@ -443,14 +442,19 @@ void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_int struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; + + /* Register once: FreeBSD does not trigger NOTE_TRIGGER on initial EV_ADD. + * EV_CLEAR resets each delivered wakeup while keeping the event registered. */ + struct kevent event; + EV_SET(&event, (uintptr_t) internal_cb, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, internal_cb); + kevent(internal_cb->loop->fd, &event, 1, NULL, 0, NULL); } void us_internal_async_wakeup(struct us_internal_async *a) { struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; - /* In kqueue you really only need to add a triggered oneshot event */ struct kevent event; - EV_SET(&event, (uintptr_t) internal_cb, EVFILT_USER, EV_ADD | EV_ONESHOT, NOTE_TRIGGER, 0, internal_cb); + EV_SET(&event, (uintptr_t) internal_cb, EVFILT_USER, 0, NOTE_TRIGGER, 0, internal_cb); kevent(internal_cb->loop->fd, &event, 1, NULL, 0, NULL); } #endif