ext/libev/ev_epoll.c in rev-0.2.2 vs ext/libev/ev_epoll.c in rev-0.2.3
- old
+ new
@@ -50,53 +50,71 @@
* limits the applicability over poll, so this is not a generic
* poll replacement.
*
* lots of "weird code" and complication handling in this file is due
* to these design problems with epoll, as we try very hard to avoid
- * epoll_ctl syscalls for common usage patterns.
+ * epoll_ctl syscalls for common usage patterns and handle the breakage
+ * ensuing from receiving events for closed and otherwise long gone
+ * file descriptors.
*/
#include <sys/epoll.h>
static void
epoll_modify (EV_P_ int fd, int oev, int nev)
{
struct epoll_event ev;
+ unsigned char oldmask;
/*
* we handle EPOLL_CTL_DEL by ignoring it here
* on the assumption that the fd is gone anyways
* if that is wrong, we have to handle the spurious
* event in epoll_poll.
+ * if the fd is added again, we try to ADD it, and, if that
+ * fails, we assume it still has the same eventmask.
*/
if (!nev)
return;
- ev.data.u64 = fd; /* use u64 to fully initialise the struct, for nicer strace etc. */
+ oldmask = anfds [fd].emask;
+ anfds [fd].emask = nev;
+
+ /* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */
+ ev.data.u64 = (uint64_t)(uint32_t)fd
+ | ((uint64_t)(uint32_t)++anfds [fd].egen << 32);
ev.events = (nev & EV_READ ? EPOLLIN : 0)
| (nev & EV_WRITE ? EPOLLOUT : 0);
if (expect_true (!epoll_ctl (backend_fd, oev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev)))
return;
if (expect_true (errno == ENOENT))
{
- /* on ENOENT the fd went away, so try to do the right thing */
+ /* if ENOENT then the fd went away, so try to do the right thing */
if (!nev)
- return;
+ goto dec_egen;
if (!epoll_ctl (backend_fd, EPOLL_CTL_ADD, fd, &ev))
return;
}
else if (expect_true (errno == EEXIST))
{
- /* on EEXIST we ignored a previous DEL */
+ /* EEXIST means we ignored a previous DEL, but the fd is still active */
+ /* if the kernel mask is the same as the new mask, we assume it hasn't changed */
+ if (oldmask == nev)
+ goto dec_egen;
+
if (!epoll_ctl (backend_fd, EPOLL_CTL_MOD, fd, &ev))
return;
}
fd_kill (EV_A_ fd);
+
+dec_egen:
+ /* we didn't successfully call epoll_ctl, so decrement the generation counter again */
+ --anfds [fd].egen;
}
static void
epoll_poll (EV_P_ ev_tstamp timeout)
{
@@ -104,31 +122,46 @@
int eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, (int)ceil (timeout * 1000.));
if (expect_false (eventcnt < 0))
{
if (errno != EINTR)
- syserr ("(libev) epoll_wait");
+ ev_syserr ("(libev) epoll_wait");
return;
}
for (i = 0; i < eventcnt; ++i)
{
struct epoll_event *ev = epoll_events + i;
- int fd = ev->data.u64;
+ int fd = (uint32_t)ev->data.u64; /* mask out the lower 32 bits */
+ int want = anfds [fd].events;
int got = (ev->events & (EPOLLOUT | EPOLLERR | EPOLLHUP) ? EV_WRITE : 0)
| (ev->events & (EPOLLIN | EPOLLERR | EPOLLHUP) ? EV_READ : 0);
- int want = anfds [fd].events;
+ /* check for spurious notification */
+ if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
+ {
+ /* recreate kernel state */
+ postfork = 1;
+ continue;
+ }
+
if (expect_false (got & ~want))
{
+ anfds [fd].emask = want;
+
/* we received an event but are not interested in it, try mod or del */
+ /* I don't think we ever need MOD, but let's handle it anyways */
ev->events = (want & EV_READ ? EPOLLIN : 0)
| (want & EV_WRITE ? EPOLLOUT : 0);
- epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev);
+ if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
+ {
+ postfork = 1; /* an error occured, recreate kernel state */
+ continue;
+ }
}
fd_event (EV_A_ fd, got);
}
@@ -153,11 +186,11 @@
backend_fudge = 0.; /* kernel sources seem to indicate this to be zero */
backend_modify = epoll_modify;
backend_poll = epoll_poll;
- epoll_eventmax = 64; /* intiial number of events receivable per poll */
+ epoll_eventmax = 64; /* initial number of events receivable per poll */
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax);
return EVBACKEND_EPOLL;
}
@@ -171,10 +204,10 @@
epoll_fork (EV_P)
{
close (backend_fd);
while ((backend_fd = epoll_create (256)) < 0)
- syserr ("(libev) epoll_create");
+ ev_syserr ("(libev) epoll_create");
fcntl (backend_fd, F_SETFD, FD_CLOEXEC);
fd_rearm_all (EV_A);
}