summaryrefslogtreecommitdiff
path: root/package/broadcom-wl/patches/100-timer_fix.patch
blob: d1ce9c98219c49b29a654683215775e725735d27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
--- 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);