|
@@ -800,6 +800,76 @@ void timer_allow_del(void)
|
|
|
}
|
|
|
|
|
|
|
|
|
+#define timer_sanity_check( \
|
|
|
+ h, tl1, tl2, last_tl1, last_tl2, dir1, dir2, dir1str, dir2str) \
|
|
|
+ (last_tl1) = (void *)h; \
|
|
|
+ (tl1) = h->dir1; \
|
|
|
+ do { \
|
|
|
+ if((tl1) == NULL) { \
|
|
|
+ int fixed = 0; \
|
|
|
+ LM_WARN("timer %s list is broken at cell %p with %s=%p " \
|
|
|
+ "%s=%p; trying to fix it...", \
|
|
|
+ dir1str, (last_tl1), dir1str, (last_tl1)->dir1, dir2str, \
|
|
|
+ (last_tl1)->dir2); \
|
|
|
+ (last_tl2) = (void *)h; \
|
|
|
+ (tl2) = h->dir2; \
|
|
|
+ do { \
|
|
|
+ if((tl2) == NULL) { \
|
|
|
+ LM_CRIT("timer %s list is broken too at cell %p with " \
|
|
|
+ "%s=%p %s=%p; abort", \
|
|
|
+ dir2str, (last_tl2), dir1str, (last_tl2)->dir1, \
|
|
|
+ dir2str, (last_tl2)->dir2); \
|
|
|
+ return 0; \
|
|
|
+ } \
|
|
|
+ if((last_tl1) == (tl2)) { \
|
|
|
+ fixed = 1; \
|
|
|
+ (last_tl1)->dir1 = (void *)(last_tl2); \
|
|
|
+ (tl1) = (void *)(last_tl2); \
|
|
|
+ LM_WARN("timer %s list recovered at cell %p with " \
|
|
|
+ "%s=%p %s=%p; continue sanity check...", \
|
|
|
+ dir1str, (last_tl1), dir1str, (last_tl1)->dir1, \
|
|
|
+ dir2str, (last_tl1)->dir2); \
|
|
|
+ break; \
|
|
|
+ } \
|
|
|
+ \
|
|
|
+ (last_tl2) = (tl2); \
|
|
|
+ (tl2) = (tl2)->dir2; \
|
|
|
+ } while((tl2) != (void *)h); \
|
|
|
+ if(!fixed) { \
|
|
|
+ LM_CRIT("timer %s list did not find the %s list broken " \
|
|
|
+ "cell " \
|
|
|
+ "%p with " \
|
|
|
+ "%s=%p %s=%p; abort", \
|
|
|
+ dir2str, dir1str, (last_tl1), dir1str, \
|
|
|
+ (last_tl1)->dir1, dir2str, (last_tl1)->dir2); \
|
|
|
+ return 0; \
|
|
|
+ } \
|
|
|
+ } \
|
|
|
+ (last_tl1) = (tl1); \
|
|
|
+ (tl1) = (tl1)->dir1; \
|
|
|
+ } while((tl1) != (void *)h)
|
|
|
+
|
|
|
+
|
|
|
+inline static int timer_list_sanity_check(struct timer_head *h)
|
|
|
+{
|
|
|
+ struct timer_ln *tl1 = NULL;
|
|
|
+ struct timer_ln *tl2 = NULL;
|
|
|
+ struct timer_ln *last_tl1 = NULL;
|
|
|
+ struct timer_ln *last_tl2 = NULL;
|
|
|
+
|
|
|
+ if(h == NULL) {
|
|
|
+ LM_CRIT("NULL timer head; abort");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ timer_sanity_check(
|
|
|
+ h, tl1, tl2, last_tl1, last_tl2, next, prev, "next", "prev");
|
|
|
+ timer_sanity_check(
|
|
|
+ h, tl1, tl2, last_tl1, last_tl2, prev, next, "prev", "next");
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
/* called from timer_handle, must be called with the timer lock held
|
|
|
* WARNING: expired one shot timers are _not_ automatically reinit
|
|
|
* (because they could have been already freed from the timer
|
|
@@ -820,6 +890,12 @@ inline static void timer_list_expire(ticks_t t, struct timer_head *h
|
|
|
first = h->next;
|
|
|
#endif
|
|
|
|
|
|
+ // check if timer circular double linked list has broken links
|
|
|
+ // try to recover if so, otherwise abort execution
|
|
|
+ if(!timer_list_sanity_check(h)) {
|
|
|
+ abort();
|
|
|
+ }
|
|
|
+
|
|
|
/*LM_DBG("@ ticks = %lu, list =%p\n",
|
|
|
(unsigned long) *ticks, h);
|
|
|
*/
|
|
@@ -921,7 +997,7 @@ static void timer_handler(void)
|
|
|
for(prev_ticks = prev_ticks + 1; prev_ticks != saved_ticks;
|
|
|
prev_ticks++)
|
|
|
timer_run(prev_ticks);
|
|
|
- timer_run(prev_ticks); /* do it for saved_ticks too */
|
|
|
+ timer_run(prev_ticks); /* do it for saved_ticks too */
|
|
|
} while(saved_ticks != *ticks); /* in case *ticks changed */
|
|
|
#ifdef USE_SLOW_TIMER
|
|
|
timer_list_expire(
|