0

In kqueue it's possible to submit a kevent with EV_ONESHOT or EV_DISPATCH flags. The first deletes the kevent after first event delivery, and the latter disables the kevent after first event delivery.

The observable effect with both flags is that subsequent events will not be delivered unless the kevent is re-added or re-enabled, respectively.

Are there other observable effects between EV_ONESHOT and EV_DISPATCH and how to choose one over the other?

The manual page for disabling a kevent using EV_DISABLE mentions that The filter itself is not disabled. Then, if I choose to use EV_DISPATCH, the filter itself is not disabled. I don’t understand what effect that has. Could someone explain?

user2962393
  • 1,083
  • 9
  • 12

1 Answers1

0

I'll answer my own question after having done more research.

I recommend reading the 2001 USENIX paper "Kqueue: A generic and scalable event notification facility” at http://people.freebsd.org/~jlemon/papers/kqueue.pdf.

When kernel receives a kevent from user, it creates a knote to represent the subscription and a filter to deal with the event source. The filter is invoked on event source activity such as packet arrival or signal delivery. The knote is added to an active list for event delivery to user if the filter indicates so and the knote is enabled. This is enough to understand the difference between EV_ONESHOT and EV_DISPATCH.

EV_ONESHOT causes the knote and the filter to be deleted after first event delivery. Thus the filter can no longer be called on event source activity. EV_DISPATCH causes the knote to be marked disabled after first event delivery, but the filter will remain to process event source activity. Whether this is a meaningful difference depends on the filter being used. Additionally, enable/disable is a faster operation than add/delete. The USENIX paper has some graphs on the performance.

I wrote a program (tested on macOS) to demonstrate the difference using EVFILT_SIGNAL. Maybe someone will find this useful in the future.

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>

int main()
{
        struct timespec timeout = {.tv_sec = 0, .tv_nsec = 0};
        sigblock(sigmask(SIGUSR1));
        pid_t pid = getpid();
        int kq = kqueue();
        struct kevent e;

        EV_SET(&e, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ENABLE | EV_DISPATCH, 0, 0, NULL);
        // EV_SET(&e, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, NULL);
        kevent(kq, &e, 1, NULL, 0, NULL);
        kill(pid, SIGUSR1);
        kevent(kq, NULL, 0, &e, 1, &timeout);
        printf("%jd\n", e.data);

        // When EV_DISPATCH is used, the filter continues to record
        // event source activity. When EV_ONESHOT is used, there is no
        // longer a filter to record activity. In either way, kernel
        // doesn't deliver events to user.
        kill(pid, SIGUSR1);
        kill(pid, SIGUSR1);
        kill(pid, SIGUSR1);

        EV_SET(&e, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0, NULL);
        kevent(kq, &e, 1, &e, 1, &timeout);

        // Prints 3 or 0 with EV_DISPATCH or EV_ONESHOT, respectively.
        printf("%jd\n", e.data);
        close(kq);
}
user2962393
  • 1,083
  • 9
  • 12