mirror of https://mirror.osredm.com/root/redis.git
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.
This commit is contained in:
parent
5e3a15ae1b
commit
175a9e3199
17
src/ae.c
17
src/ae.c
|
@ -239,7 +239,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
|
|||
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.
|
||||
*
|
||||
* Note that's O(N) since time events are unsorted.
|
||||
|
@ -248,7 +248,7 @@ int aeDeleteTimeEvent(aeEventLoop *eventLoop, long long id)
|
|||
* 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)).
|
||||
*/
|
||||
static long msUntilEarliestTimer(aeEventLoop *eventLoop) {
|
||||
static int64_t usUntilEarliestTimer(aeEventLoop *eventLoop) {
|
||||
aeTimeEvent *te = eventLoop->timeEventHead;
|
||||
if (te == NULL) return -1;
|
||||
|
||||
|
@ -260,8 +260,7 @@ static long msUntilEarliestTimer(aeEventLoop *eventLoop) {
|
|||
}
|
||||
|
||||
monotime now = getMonotonicUs();
|
||||
return (now >= earliest->when)
|
||||
? 0 : (long)((earliest->when - now) / 1000);
|
||||
return (now >= earliest->when) ? 0 : earliest->when - now;
|
||||
}
|
||||
|
||||
/* Process time events */
|
||||
|
@ -361,14 +360,14 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
|||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
|
||||
int j;
|
||||
struct timeval tv, *tvp;
|
||||
long msUntilTimer = -1;
|
||||
int64_t usUntilTimer = -1;
|
||||
|
||||
if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
|
||||
msUntilTimer = msUntilEarliestTimer(eventLoop);
|
||||
usUntilTimer = usUntilEarliestTimer(eventLoop);
|
||||
|
||||
if (msUntilTimer >= 0) {
|
||||
tv.tv_sec = msUntilTimer / 1000;
|
||||
tv.tv_usec = (msUntilTimer % 1000) * 1000;
|
||||
if (usUntilTimer >= 0) {
|
||||
tv.tv_sec = usUntilTimer / 1000000;
|
||||
tv.tv_usec = usUntilTimer % 1000000;
|
||||
tvp = &tv;
|
||||
} else {
|
||||
/* If we have to check for events but need to return
|
||||
|
|
|
@ -111,7 +111,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
|||
int retval, numevents = 0;
|
||||
|
||||
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) {
|
||||
int j;
|
||||
|
||||
|
|
Loading…
Reference in New Issue