You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

51 lines
1.8 KiB

--- 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);