Modern poller, including epoll, kqueue or vpp_vcl_epoll are message queues!
# Construtor
epoll: epoll_create() returns fd
kqueue: kqueue() return fd
vpp_vcl_epoll: created within struct vcl_worker, request for event_fd
# Notification
How do poller owner wait for the next message(s)?
epoll: epoll_wait()
kqueue: kevent(int kq, ..., struct kevent *eventlist,...)
vpp_vcl_epoll: vppcom_epoll_wait()
Advance: you can actually stack fd into another poller and wait.
# subscribe
epoll: epoll_ctl() to mutate the topics
kqueue: kevent(int kq, const struct kevent *changelist, ...)
vpp_vcl_epoll: vppcom_epoll_ctl()
# publish
epoll: linux kernel
kqueue: BSD kernel
vpp_vcl_epoll: struct vcl_worker_