Detect infinite loop in scheduler

If more timeouts were handled than there were in the timer queue on
start, assume some code is scheduling timeouts with negative delays and
abort. Make the actual limit higher in case the machine is temporarily
overloaded and dispatching the handlers takes more time than was delay
of a scheduled timeout.
This commit is contained in:
Miroslav Lichvar 2011-06-23 13:47:55 +02:00
parent bab7ba22cf
commit dce2366b3a

43
sched.c
View file

@ -430,18 +430,26 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
}
/* ================================================== */
/* The current time (now) has to be passed in from the
caller to avoid race conditions */
/* Try to dispatch any timeouts that have already gone by, and
keep going until all are done. (The earlier ones may take so
long to do that the later ones come around by the time they are
completed). */
static int
static void
dispatch_timeouts(struct timeval *now) {
TimerQueueEntry *ptr;
SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg;
int n_done = 0;
int n_done = 0, n_entries_on_start = n_timer_queue_entries;
while (1) {
LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 &&
UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) {
break;
}
if ((n_timer_queue_entries > 0) &&
(UTI_CompareTimevals(now, &(timer_queue.next->tv)) >= 0)) {
ptr = timer_queue.next;
last_class_dispatch[ptr->class] = *now;
@ -456,10 +464,16 @@ dispatch_timeouts(struct timeval *now) {
/* Increment count of timeouts handled */
++n_done;
/* If more timeouts were handled than there were in the timer queue on
start, assume some code is scheduling timeouts with negative delays and
abort. Make the actual limit higher in case the machine is temporarily
overloaded and dispatching the handlers takes more time than was delay
of a scheduled timeout. */
if (n_done > n_entries_on_start * 4) {
LOG_FATAL(LOGF_Scheduler, "Possible infinite loop in scheduling");
}
}
return n_done;
}
/* ================================================== */
@ -562,20 +576,15 @@ SCH_MainLoop(void)
/* Copy current set of read file descriptors */
memcpy((void *) &rd, (void *) &read_fds, sizeof(fd_set));
/* Try to dispatch any timeouts that have already gone by, and
keep going until all are done. (The earlier ones may take so
long to do that the later ones come around by the time they are
completed). */
do {
LCL_ReadRawTime(&now);
} while (dispatch_timeouts(&now) > 0);
/* Dispatch timeouts and fill now with current raw time */
dispatch_timeouts(&now);
/* Check whether there is a timeout and set it up */
if (n_timer_queue_entries > 0) {
UTI_DiffTimevals(&tv, &(timer_queue.next->tv), &now);
ptv = &tv;
assert(tv.tv_sec > 0 || tv.tv_usec > 0);
} else {
ptv = NULL;