Fix bug #198 - Timers could cause infinite loops
This could essentially happen due to time drift, high load, or the process being put in sleep for a while. The reason is that recurring timers could be added to the same time slot as the timeslot being handled.
This commit is contained in:
parent
d73d213bc4
commit
550740f715
|
@ -42,6 +42,7 @@ void timeout_queue_initialize(struct timeout_queue* t, time_t now, size_t max)
|
||||||
{
|
{
|
||||||
t->last = now;
|
t->last = now;
|
||||||
t->max = max;
|
t->max = max;
|
||||||
|
memset(&t->lock, 0, sizeof(t->lock));
|
||||||
t->events = hub_malloc_zero(max * sizeof(struct timeout_evt*));
|
t->events = hub_malloc_zero(max * sizeof(struct timeout_evt*));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +53,56 @@ void timeout_queue_shutdown(struct timeout_queue* t)
|
||||||
t->max = 0;
|
t->max = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int timeout_queue_locked(struct timeout_queue* t)
|
||||||
|
{
|
||||||
|
return t->lock.ptr != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int timeout_queue_lock(struct timeout_queue* t)
|
||||||
|
{
|
||||||
|
t->lock.ptr = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock and flush the locked events to the main timeout queue.
|
||||||
|
static int timeout_queue_unlock(struct timeout_queue* t)
|
||||||
|
{
|
||||||
|
struct timeout_evt* evt, *tmp, *first;
|
||||||
|
size_t pos;
|
||||||
|
t->lock.ptr = NULL;
|
||||||
|
|
||||||
|
evt = t->lock.next;
|
||||||
|
while (evt)
|
||||||
|
{
|
||||||
|
tmp = evt->next;
|
||||||
|
pos = evt->timestamp % t->max;
|
||||||
|
first = t->events[pos];
|
||||||
|
if (first)
|
||||||
|
{
|
||||||
|
first->prev->next = evt;
|
||||||
|
evt->prev = first->prev;
|
||||||
|
first->prev = evt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t->events[pos] = evt;
|
||||||
|
evt->prev = evt;
|
||||||
|
}
|
||||||
|
evt->next = 0;
|
||||||
|
evt = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
t->lock.next = 0;
|
||||||
|
t->lock.prev = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t timeout_queue_process(struct timeout_queue* t, time_t now)
|
size_t timeout_queue_process(struct timeout_queue* t, time_t now)
|
||||||
{
|
{
|
||||||
size_t pos = (size_t) t->last;
|
size_t pos = (size_t) t->last;
|
||||||
size_t events = 0;
|
size_t events = 0;
|
||||||
struct timeout_evt* evt = 0;
|
struct timeout_evt* evt = 0;
|
||||||
t->last = now;
|
t->last = now;
|
||||||
|
timeout_queue_lock(t);
|
||||||
for (; pos <= now; pos++)
|
for (; pos <= now; pos++)
|
||||||
{
|
{
|
||||||
while ((evt = t->events[pos % t->max]))
|
while ((evt = t->events[pos % t->max]))
|
||||||
|
@ -67,6 +112,7 @@ size_t timeout_queue_process(struct timeout_queue* t, time_t now)
|
||||||
events++;
|
events++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
timeout_queue_unlock(t);
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,6 +128,61 @@ size_t timeout_queue_get_next_timeout(struct timeout_queue* t, time_t now)
|
||||||
return seconds;
|
return seconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void timeout_queue_insert_locked(struct timeout_queue* t, struct timeout_evt* evt)
|
||||||
|
{
|
||||||
|
/* All events point back to the sentinel.
|
||||||
|
* this means the event is considered schedule (see timeout_evt_is_scheduled),
|
||||||
|
* and it is easy to tell if the event is in the wait queue or not.
|
||||||
|
*/
|
||||||
|
evt->prev = &t->lock;
|
||||||
|
evt->next = NULL;
|
||||||
|
|
||||||
|
// The sentinel next points to the first event in the locked queue
|
||||||
|
// The sentinel prev points to the last evetnt in the locked queue.
|
||||||
|
// NOTE: if prev is != NULL then next also must be != NULL.
|
||||||
|
if (t->lock.prev)
|
||||||
|
{
|
||||||
|
t->lock.prev->next = evt;
|
||||||
|
t->lock.prev = evt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t->lock.next = evt;
|
||||||
|
t->lock.prev = evt;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timeout_queue_remove_locked(struct timeout_queue* t, struct timeout_evt* evt)
|
||||||
|
{
|
||||||
|
uhub_assert(evt->prev == &t->lock);
|
||||||
|
if (t->lock.next == evt)
|
||||||
|
{
|
||||||
|
t->lock.next = evt->next;
|
||||||
|
if (t->lock.prev == evt)
|
||||||
|
t->lock.prev = evt->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct timeout_evt *prev, *it;
|
||||||
|
prev = 0;
|
||||||
|
it = t->lock.next;
|
||||||
|
while (it)
|
||||||
|
{
|
||||||
|
prev = it;
|
||||||
|
it = it->next;
|
||||||
|
if (it == evt)
|
||||||
|
{
|
||||||
|
prev->next = it->next;
|
||||||
|
if (!prev->next)
|
||||||
|
t->lock.prev = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timeout_evt_reset(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size_t seconds)
|
void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size_t seconds)
|
||||||
{
|
{
|
||||||
|
@ -90,6 +191,12 @@ void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size
|
||||||
evt->timestamp = t->last + seconds;
|
evt->timestamp = t->last + seconds;
|
||||||
evt->next = 0;
|
evt->next = 0;
|
||||||
|
|
||||||
|
if (timeout_queue_locked(t))
|
||||||
|
{
|
||||||
|
timeout_queue_insert_locked(t, evt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
first = t->events[pos];
|
first = t->events[pos];
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
|
@ -112,6 +219,13 @@ void timeout_queue_remove(struct timeout_queue* t, struct timeout_evt* evt)
|
||||||
size_t pos = (evt->timestamp % t->max);
|
size_t pos = (evt->timestamp % t->max);
|
||||||
struct timeout_evt* first = t->events[pos];
|
struct timeout_evt* first = t->events[pos];
|
||||||
|
|
||||||
|
// Removing a locked event
|
||||||
|
if (evt->prev == &t->lock)
|
||||||
|
{
|
||||||
|
timeout_queue_remove_locked(t, evt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!first || !evt->prev)
|
if (!first || !evt->prev)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ struct timeout_queue
|
||||||
{
|
{
|
||||||
time_t last;
|
time_t last;
|
||||||
size_t max;
|
size_t max;
|
||||||
|
struct timeout_evt lock;
|
||||||
struct timeout_evt** events;
|
struct timeout_evt** events;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue