mirror of
https://github.com/libevent/libevent.git
synced 2025-01-31 09:12:55 +08:00
Avoid calling read(2) on eventfd on each event-loop wakeup
Register the eventfd with EPOLLET to enable edge-triggered notification where we don't need to read the data from the eventfd for every wakeup event. When the eventfd counter reaches the maximum value of the unsigned 64-bit, we rewind the counter and retry again. This optimization saves one system call on each event-loop wakeup, which eliminates the extra latency for epoll as the EVFILT_USER filter does for the kqueue.
This commit is contained in:
parent
e0a4574ba2
commit
6074d55822
37
event.c
37
event.c
@ -2589,13 +2589,31 @@ evthread_notify_base_default(struct event_base *base)
|
|||||||
static int
|
static int
|
||||||
evthread_notify_base_eventfd(struct event_base *base)
|
evthread_notify_base_eventfd(struct event_base *base)
|
||||||
{
|
{
|
||||||
|
int efd = base->th_notify_fd[0];
|
||||||
ev_uint64_t msg = 1;
|
ev_uint64_t msg = 1;
|
||||||
int r;
|
ev_uint64_t val;
|
||||||
do {
|
|
||||||
r = write(base->th_notify_fd[0], (void*) &msg, sizeof(msg));
|
|
||||||
} while (r < 0 && errno == EAGAIN);
|
|
||||||
|
|
||||||
return (r < 0) ? -1 : 0;
|
int ret;
|
||||||
|
for (;;) {
|
||||||
|
ret = eventfd_write(efd, (eventfd_t) msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
// When EAGAIN occurs, the eventfd counter hits the maximum value of the unsigned 64-bit.
|
||||||
|
// We need to first drain the eventfd and then write again.
|
||||||
|
//
|
||||||
|
// Check out https://man7.org/linux/man-pages/man2/eventfd.2.html for details.
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
// It's ready to retry.
|
||||||
|
if (eventfd_read(efd, &val) == 0 || errno == EAGAIN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Unknown error occurs.
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3648,14 +3666,7 @@ event_set_mem_functions(void *(*malloc_fn)(size_t sz),
|
|||||||
static void
|
static void
|
||||||
evthread_notify_drain_eventfd(evutil_socket_t fd, short what, void *arg)
|
evthread_notify_drain_eventfd(evutil_socket_t fd, short what, void *arg)
|
||||||
{
|
{
|
||||||
ev_uint64_t msg;
|
|
||||||
ev_ssize_t r;
|
|
||||||
struct event_base *base = arg;
|
struct event_base *base = arg;
|
||||||
|
|
||||||
r = read(fd, (void*) &msg, sizeof(msg));
|
|
||||||
if (r<0 && errno != EAGAIN) {
|
|
||||||
event_sock_warn(fd, "Error reading from eventfd");
|
|
||||||
}
|
|
||||||
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
|
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
|
||||||
base->is_notify_pending = 0;
|
base->is_notify_pending = 0;
|
||||||
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
||||||
@ -3733,7 +3744,7 @@ evthread_make_base_notifiable_nolock_(struct event_base *base)
|
|||||||
|
|
||||||
/* prepare an event that we can use for wakeup */
|
/* prepare an event that we can use for wakeup */
|
||||||
event_assign(&base->th_notify, base, base->th_notify_fd[0],
|
event_assign(&base->th_notify, base, base->th_notify_fd[0],
|
||||||
EV_READ|EV_PERSIST, cb, base);
|
EV_READ|EV_PERSIST|EV_ET, cb, base);
|
||||||
|
|
||||||
/* we need to mark this as internal event */
|
/* we need to mark this as internal event */
|
||||||
base->th_notify.ev_flags |= EVLIST_INTERNAL;
|
base->th_notify.ev_flags |= EVLIST_INTERNAL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user