Bladeren bron

- shoot-yourself-in-the-foot bug reporting and workarround (time_del(self) in a timer handle)
- added timer_allow_del() - use with care
- updated timer docs
- tm: uses timer_allow_del() in fr (as a safeguard)

Andrei Pelinescu-Onciul 20 jaren geleden
bovenliggende
commit
0f283a6823
4 gewijzigde bestanden met toevoegingen van 99 en 5 verwijderingen
  1. 21 4
      doc/timers.txt
  2. 4 0
      modules/tm/timer.c
  3. 72 1
      timer.c
  4. 2 0
      timer.h

+ 21 - 4
doc/timers.txt

@@ -114,7 +114,8 @@ WARNING: do not initialize/re-initialize a running timer!
 
 To remove a timer from the active timer list call timer_del(struct timer_ln*).
 timer_del is the slowest of all the timer functions. If you are trying to delete a timer whose timer is executing. timer_del will wait until it finishes.
-WARNING: if you have an one shot timer that frees its memory before exiting, make sure you don't call timer_del afterwards (e.g. use some reference counters and free the memory only if the counter is 0).
+WARNING: - avoid deleting your own timer from its timer handle (if you try it, you'll get a BUG message in the log). If you _must_ have this, you have a broken design. If you still want it, look at  timer_allow_del().
+         - if you have an one shot timer that frees its memory before exiting, make sure you don't call timer_del afterwards (e.g. use some reference counters and free the memory only if the counter is 0).
 
 Race example (using the struct foo defined above):
 
@@ -139,14 +140,22 @@ timer_del(&f->timer); /* if the timer is already expired => f is already
 
 The above timer_del/free_in_one_shot_timer race example is very simple, but consider that you can have much more complex scenarios, when timer_del can be called from different processes on some asynchronous events. If this looks like you're intended usage, make sure you use some  reference counters or some other protection mechanism to avoid the above race.
 
+4.5 timer_allow_del
+-------------------
 
-4.5 Getting the ticks value
+Marks a timer as "to be deleted when the handler ends", usefull when the timer handler knows it won't prolong the timer anymore (it will return 0) and will do some time consuming work. Calling this function will cause simultaneous timer_dels to return immediately (they won't  wait anymore for the timer handle to finish). It will also allow  self-deleting from the timer handle without logging a bug.
+ WARNING: - if you rely on timer_del to know when the timer handle execution finishes (e.g. to free resources used in the timer handle), don't use this function.
+          - call this function only if this is your last timer handle run (the timer handle will return 0)
+          - this function can be called only from a timer handle (in timer context), all other calls will have no effect and will log a bug message
+
+
+4.6 Getting the ticks value
 ----------------------------
 
 If you need the current ticks value you can get with get_ticks_raw().
 WARNING: don't use get_ticks(). get_ticks() returns the number of ticks converted to seconds and it was kept only for compatibility reasons with the existing code.
 
-4.6 Conversion
+4.7 Conversion
 ---------------
 
 To convert between ticks & time and viceversa, include timer_ticks.h and use
@@ -161,12 +170,20 @@ TICKS_TO_MS(t)  /* converts from ticks to milliseconds, can overflow for
 TICKS_TO_S(t)  /* converts from ticks to s, rounded down */
 
 
-4.7 Backward compatibility
+4.8 Backward compatibility
 --------------------------
 
 The old  register_timer and get_ticks() are still supported for backward compatibility. This means that you don't have to change your existing working code.
 
 
+5.0 Debugging
+-------------
+
+The timers have built-in debugging information. To activate it you only need to define TIMER_DEBUG (recompile ser with make CC_EXTRA_OPTS=-DTIMER_DEBUG all).
+The timer debug log level is by default L_WARN. [ FIXME: add it as script option]
+TIMER_DEBUG enables extra sanity checks and it will log a lot of information
+ (like the caller of timer_* functions, where a timer was added a.s.o).
+
 
 [Todo]:
 - SLOW, DRIFT, RESYNC, FREQUENCY

+ 4 - 0
modules/tm/timer.c

@@ -99,6 +99,8 @@
  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced;
  *              timer_link.payload removed (bogdan)
  *  2005-10-03  almost completely rewritten to use the new timers (andrei)
+ *  2005-12-12  on final response marked the rb as removed to avoid deleting
+ *              it from the timer handle; timer_allow_del()  (andrei)
  */
 
 #include "defs.h"
@@ -423,6 +425,8 @@ ticks_t retr_buf_handler(ticks_t ticks, struct timer_ln* tl, void *p)
 							 (both timers disabled)
 							  a little race risk, but
 							  nothing bad would happen */
+		timer_allow_del(); /* [optional] allow timer_dels, since we're done
+							  and there is no race risk */
 		final_response_handler(rbuf, t);
 		return 0;
 	}else{

+ 72 - 1
timer.c

@@ -29,6 +29,8 @@
  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
  *  2003-03-29  cleaning pkg_mallocs introduced (jiri)
  *  2005-07-27  complete re-design/re-implementation (andrei)
+ *  2005-12-12  workarround & bug reporting for timer_del(self) called from
+ *              a timer handle; added timer_allow_del()  (andrei)
  */
 
 
@@ -80,6 +82,9 @@ static int timer_id=0;
 
 static gen_lock_t* timer_lock=0;
 static struct timer_ln* volatile* running_timer=0;/* running timer handler */
+static int in_timer=0;
+
+#define IS_IN_TIMER() (in_timer)
 
 #define LOCK_TIMER_LIST()		lock_get(timer_lock)
 #define UNLOCK_TIMER_LIST()		lock_release(timer_lock)
@@ -111,6 +116,9 @@ static struct timer_ln* volatile* running_timer2=0; /* timer handler running
 													     in the "slow" timer */
 static sigset_t slow_timer_sset;
 pid_t slow_timer_pid;
+static int in_slow_timer=0;
+
+#define IS_IN_TIMER_SLOW() (in_slow_timer)
 #define SET_RUNNING_SLOW(t)		(*running_timer2=(t))
 #define IS_RUNNING_SLOW(t)		(*running_timer2==(t))
 #define UNSET_RUNNING_SLOW()	(*running_timer2=0)
@@ -564,6 +572,23 @@ again:
 			}
 			if (IS_RUNNING_SLOW(tl)){
 				UNLOCK_SLOW_TIMER_LIST();
+				if (IS_IN_TIMER_SLOW()){
+					/* if somebody tries to shoot himself in the foot,
+					 * warn him and ignore the delete */
+					LOG(L_CRIT, "BUG: timer handle %p (s) tried to delete"
+							" itself\n", tl);
+#ifdef TIMER_DEBUG
+					LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n",
+									func, file, line);
+					LOG(timerlog, "WARN: -timer_del-: added %d times"
+						", last from: %s(%s):%d, deleted %d times"
+						", last from: %s(%s):%d, init %d times, expired %d \n",
+						tl->add_calls, tl->add_func, tl->add_file,
+						tl->add_line, tl->del_calls, tl->del_func, 
+						tl->del_file, tl->del_line, tl->init, tl->expires_no);
+#endif
+					return; /* do nothing */
+				}
 				sched_yield(); /* wait for it to complete */
 				goto again;
 			}
@@ -613,6 +638,23 @@ again:
 #endif
 			if (IS_RUNNING(tl)){
 				UNLOCK_TIMER_LIST();
+				if (IS_IN_TIMER()){
+					/* if somebody tries to shoot himself in the foot,
+					 * warn him and ignore the delete */
+					LOG(L_CRIT, "BUG: timer handle %p tried to delete"
+							" itself\n", tl);
+#ifdef TIMER_DEBUG
+					LOG(timerlog, "WARN: -timer_del-: called from %s(%s):%d\n",
+									func, file, line);
+					LOG(timerlog, "WARN: -timer_del-: added %d times"
+						", last from: %s(%s):%d, deleted %d times"
+						", last from: %s(%s):%d, init %d times, expired %d \n",
+						tl->add_calls, tl->add_func, tl->add_file,
+						tl->add_line, tl->del_calls, tl->del_func, 
+						tl->del_file, tl->del_line, tl->init, tl->expires_no);
+#endif
+					return; /* do nothing */
+				}
 				sched_yield(); /* wait for it to complete */
 				goto again;
 			}
@@ -634,7 +676,7 @@ again:
 					"{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), 
 					tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
 					tl->data, tl->f, tl->flags);
-				LOG(timerlog, "WANT: -timer_del-; called from %s(%s):%d\n",
+				LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
 						func, file, line);
 				LOG(timerlog, "WARN: -timer_del-: added %d times"
 						", last from: %s(%s):%d, deleted %d times"
@@ -658,6 +700,33 @@ again:
 
 
 
+/* marks a timer as "to be deleted when the handler ends", usefull when
+ * the timer handler knows it won't prolong the timer anymore (it will 
+ * return 0) and will do some time consuming work. Calling this function
+ * will cause simultaneous timer_dels to return immediately (they won't 
+ * wait anymore for the timer handle to finish). It will also allow 
+ * self-deleting from the timer handle without bug reports.
+ * WARNING: - if you rely on timer_del to know when the timer handle execution
+ *            finishes (e.g. to free resources used in the timer handle), don't
+ *            use this function.
+ *          - this function can be called only from a timer handle (in timer
+ *            context), all other calls will have no effect and will log a
+ *            bug message
+ */
+void timer_allow_del()
+{
+	if (IS_IN_TIMER() ){
+			UNSET_RUNNING();
+	}else
+#ifdef USE_SLOW_TIMER
+	if (IS_IN_TIMER_SLOW()){
+			UNSET_RUNNING_SLOW();
+	}else 
+#endif
+		LOG(L_CRIT, "BUG: timer_allow_del called outside a timer handle\n");
+}
+
+
 /* called from timer_handle, must be called with the timer lock held */
 inline static void timer_list_expire(ticks_t t, struct timer_head* h
 #ifdef USE_SLOW_TIMER
@@ -813,6 +882,7 @@ static void timer_handler()
 /* main timer function, never exists */
 void timer_main()
 {
+	in_timer=1; /* mark this process as the fast timer */
 	while(1){
 		if (run_timer){
 			timer_handler();
@@ -915,6 +985,7 @@ void slow_timer_main()
 	struct timer_ln* tl;
 	unsigned short i;
 	
+	in_slow_timer=1; /* mark this process as the slow timer */
 	while(1){
 		n=sigwaitinfo(&slow_timer_sset, 0);
 		if (n==-1){

+ 2 - 0
timer.h

@@ -175,6 +175,8 @@ void timer_del_safe(struct timer_ln *tl);
 #define timer_del timer_del_safe
 #endif
 
+void timer_allow_del();
+
 /* old timer compatibility functions & structure */
 
 struct sr_timer{