--- a/router/shared/linux_timer.c +++ b/router/shared/linux_timer.c @@ -94,6 +94,7 @@ typedef long uclock_t; #define TFLAG_NONE 0 #define TFLAG_CANCELLED (1<<0) #define TFLAG_DELETED (1<<1) +#define TFLAG_QUEUED (1<<2) struct event { struct timeval it_interval; @@ -207,6 +208,7 @@ int timer_create( event_freelist = event->next; event->next = NULL; + event->flags &= ~TFLAG_QUEUED; check_event_queue(); @@ -387,6 +389,7 @@ int timer_settime } event->flags &= ~TFLAG_CANCELLED; + event->flags |= TFLAG_QUEUED; unblock_timer(); @@ -502,7 +505,15 @@ static void alarm_handler(int i) (*(event->func))((timer_t) event, (int)event->arg); /* If the event has been cancelled, do NOT put it back on the queue. */ - if (!(event->flags & TFLAG_CANCELLED)) { + /* Check for TFLAG_QUEUED is to avoid pathologic case, when after + * dequeueing event handler deletes its own timer and allocates new one + * which (at least in some cases) gets the same pointer and thus its + * 'flags' will be rewritten, most notably TFLAG_CANCELLED, and, to + * complete the disaster, it will be queued. alarm_handler tries to + * enqueue 'event' (which is on the same memory position as newly + * allocated timer), which results in queueing the same pointer once + * more. And this way, loop in event queue is created. */ + if ( !(event->flags & TFLAG_CANCELLED) && !(event->flags & TFLAG_QUEUED) ) { /* if the event is a recurring event, reset the timer and * find its correct place in the sorted list of events. @@ -545,6 +556,7 @@ static void alarm_handler(int i) /* link our new event into the pending event queue. */ event->next = *ppevent; *ppevent = event; + event->flags |= TFLAG_QUEUED; } else { /* there is no interval, so recycle the event structure. * timer_delete((timer_t) event);