From f9f4d4fe1720f888ff1d89d7edfdc9725f62432d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 11 Apr 2008 20:02:50 +0000 Subject: [PATCH] r19309@catbus: nickm | 2008-04-11 16:02:07 -0400 Fix for epoll-on-linux bug (#1908866) where timeout values over (LONG_MAX-999)/HZ) (35 for me, or maybe 6 hours 50 min for some people, or maybe 3 hours 25 minutes for a special few) get treated as "wait forever". This actually deserves to be fixed in the kernel, but even if it is we will need to support Linux versions with this bug. svn:r709 --- ChangeLog | 1 + epoll.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/ChangeLog b/ChangeLog index 787ad684..de3ad264 100644 --- a/ChangeLog +++ b/ChangeLog @@ -64,6 +64,7 @@ Changes in current version: o do not delete uninitialized timeout event in evdns o Correct the documentation on buffer printf functions. o Don't warn on unimplemented epoll_create(): this isn't a problem, just a reason to fall back to poll or select. + o Correctly handle timeouts larger than 35 minutes on Linux with epoll.c. This is probably a kernel defect, but we'll have to support old kernels anyway even if it gets fixed. Changes in 1.4.0: diff --git a/epoll.c b/epoll.c index 67c0f469..3b58e437 100644 --- a/epoll.c +++ b/epoll.c @@ -96,6 +96,14 @@ struct eventop epollops = { #define NEVENT 32000 +/* On Linux kernels at least up to 2.6.24.4, epoll can't handle timeout + * values bigger than (LONG_MAX - 999ULL)/HZ. HZ in the wild can be + * as big as 1000, and LONG_MAX can be as small as (1<<31)-1, so the + * largest number of msec we can support here is 2147482. Let's + * round that down by 47 seconds. + */ +#define MAX_EPOLL_TIMEOUT_MSEC (35*60*1000) + static void * epoll_init(struct event_base *base) { @@ -191,6 +199,12 @@ epoll_dispatch(struct event_base *base, void *arg, struct timeval *tv) if (tv != NULL) timeout = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; + if (timeout > MAX_EPOLL_TIMEOUT_MSEC) { + /* Linux kernels can wait forever if the timeout is too big; + * see comment on MAX_EPOLL_TIMEOUT_MSEC. */ + timeout = MAX_EPOLL_TIMEOUT_MSEC; + } + res = epoll_wait(epollop->epfd, events, epollop->nevents, timeout); if (res == -1) {