Unverified Commit 175a9e31 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Fix busy loop in ae.c when timer event is about to fire (#8764)

The code used to decide on the next time to wake on a timer with
microsecond accuracy, but when deciding to go to sleep it used
milliseconds accuracy (with truncation), this means that it would wake
up too early, see that there's no timer to process, and go to sleep
again for 0ms again and again until the right microsecond arrived.

i.e. a timer for 100ms, would sleep for 99ms, but then do a busy loop
through the kernel in the last millisecond, triggering many calls to
beforeSleep.

The fix is to change all the logic in ae.c to work with microseconds,
which is good since most of the ae backends support micro (or even nano)
seconds. however the epoll backend, doesn't support micro, so to avoid
this problem it needs to round upwards, rather than truncate.

Issue created by the monotonic timer PR #7644 (redis 6.2)
Before that, all the timers in ae.c were in milliseconds (using
mstime), so when it requested the backend to sleep till the next timer
event, it would have worked ok.
parent 5e3a15ae
...@@ -239,7 +239,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) ...@@ -239,7 +239,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
return AE_ERR; /* NO event with the specified ID found */ return AE_ERR; /* NO event with the specified ID found */
} }
/* How many milliseconds until the first timer should fire. /* How many microseconds until the first timer should fire.
* If there are no timers, -1 is returned. * If there are no timers, -1 is returned.
* *
* Note that's O(N) since time events are unsorted. * Note that's O(N) since time events are unsorted.
...@@ -248,7 +248,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id) ...@@ -248,7 +248,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
* Much better but still insertion or deletion of timers is O(N). * Much better but still insertion or deletion of timers is O(N).
* 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)). * 2) Use a skiplist to have this operation as O(1) and insertion as O(log(N)).
*/ */
static long msUntilEarliestTimer(aeEventLoop *eventLoop) { static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
aeTimeEvent *te = eventLoop->timeEventHead; aeTimeEvent *te = eventLoop->timeEventHead;
if (te == NULL) return -1; if (te == NULL) return -1;
...@@ -260,8 +260,7 @@ static long msUntilEarliestTimer(aeEventLoop *eventLoop) { ...@@ -260,8 +260,7 @@ static long msUntilEarliestTimer(aeEventLoop *eventLoop) {
} }
monotime now = getMonotonicUs(); monotime now = getMonotonicUs();
return (now >= earliest->when) return (now >= earliest->when) ? 0 : earliest->when - now;
? 0 : (long)((earliest->when - now) / 1000);
} }
/* Process time events */ /* Process time events */
...@@ -361,14 +360,14 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags) ...@@ -361,14 +360,14 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) { ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j; int j;
struct timeval tv, *tvp; struct timeval tv, *tvp;
long msUntilTimer = -1; int64_t usUntilTimer = -1;
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT)) if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
msUntilTimer = msUntilEarliestTimer(eventLoop); usUntilTimer = usUntilEarliestTimer(eventLoop);
if (msUntilTimer >= 0) { if (usUntilTimer >= 0) {
tv.tv_sec = msUntilTimer / 1000; tv.tv_sec = usUntilTimer / 1000000;
tv.tv_usec = (msUntilTimer % 1000) * 1000; tv.tv_usec = usUntilTimer % 1000000;
tvp = &tv; tvp = &tv;
} else { } else {
/* If we have to check for events but need to return /* If we have to check for events but need to return
......
...@@ -111,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) { ...@@ -111,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
int retval, numevents = 0; int retval, numevents = 0;
retval = epoll_wait(state->epfd,state->events,eventLoop->setsize, retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1); tvp ? (tvp->tv_sec*1000 + (tvp->tv_usec + 999)/1000) : -1);
if (retval > 0) { if (retval > 0) {
int j; int j;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment