Browse code

New timer interface: - allows adding timers dynamically - supports one shot and periodic timers - precise - allows timeouts < 0.5 s (depends on the config., by default min. timeout = 62.5 ms) - based on a 3 level hierarchical timing wheel - very fast - low overhead for timer functions (most of the time, the excetion is timer_del which take can take quite a long time in special situations) - supports "slow" timers (compile option, by default on), "slow" timer = a timer handler which is declared as possible taking a long timer to execute. - backward compatible: old timer functions are preserverd => no changes needed for the existing code (although migration to the new interface is recommended) For more information read doc/timers.txt. tm timer update will follow shortly (this week).

Andrei Pelinescu-Onciul authored on 05/12/2005 18:29:30
Showing 14 changed files
... ...
@@ -61,7 +61,7 @@ MAIN_NAME=ser
61 61
 VERSION = 0
62 62
 PATCHLEVEL = 10
63 63
 SUBLEVEL =   99
64
-EXTRAVERSION = -dev24
64
+EXTRAVERSION = -dev25-timers
65 65
 
66 66
 RELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
67 67
 OS = $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z]" "[a-z]")
68 68
new file mode 100644
... ...
@@ -0,0 +1,111 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * circular list maintenance macros
5
+ *
6
+ * Copyright (C) 2005 iptelorg GmbH
7
+ *
8
+ * This file is part of ser, a free SIP server.
9
+ *
10
+ * ser is free software; you can redistribute it and/or modify
11
+ * it under the terms of the GNU General Public License as published by
12
+ * the Free Software Foundation; either version 2 of the License, or
13
+ * (at your option) any later version
14
+ *
15
+ * For a license to use the ser software under conditions
16
+ * other than those described here, or to purchase support for this
17
+ * software, please contact iptel.org by e-mail at the following addresses:
18
+ *    info@iptel.org
19
+ *
20
+ * ser is distributed in the hope that it will be useful,
21
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
+ * GNU General Public License for more details.
24
+ *
25
+ * You should have received a copy of the GNU General Public License 
26
+ * along with this program; if not, write to the Free Software 
27
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
+ */
29
+
30
+/* History:
31
+ * --------
32
+ *  2005-08-08  created by andrei
33
+ */
34
+
35
+#ifndef _clist_h
36
+#define _clist_h
37
+
38
+/* circular list */
39
+#define clist_init(c, next, prev) \
40
+	do{ \
41
+		(c)->next=(void*)(c); \
42
+		(c)->prev=(void*)(c); \
43
+	} while(0)
44
+
45
+
46
+
47
+/* adds an entire sublist { s,e } (including s & e )
48
+ * after head
49
+ */
50
+#define clist_insert_sublist(head, s, e, next, prev) \
51
+	do{ \
52
+		(s)->prev=(head); \
53
+		(e)->next=(head)->next; \
54
+		(e)->next->prev=(e); \
55
+		(head)->next=s;   \
56
+	}while(0)
57
+
58
+
59
+
60
+/* appends an entire sublist { s,e } (including s & e )
61
+ * at the end of the list
62
+ */
63
+#define clist_append_sublist(head, s, e, next, prev) \
64
+	do{ \
65
+		(s)->prev=(head)->prev; \
66
+		(e)->next=(void*)(head); \
67
+		(s)->prev->next=(s); \
68
+		(head)->prev=(e);   \
69
+	}while(0)
70
+
71
+
72
+
73
+/* remove sublist { s,e } (including s & e )
74
+ * always, if start is the beginning of the list use
75
+ * clist_rm_sublist(head->next, e, next, prev ) */
76
+#define clist_rm_sublist(s, e, next, prev) \
77
+	do{\
78
+		(s)->prev->next=(e)->next;  \
79
+		(e)->next->prev=(s)->prev ; \
80
+	}while(0)
81
+
82
+
83
+
84
+/* insert after (head) */
85
+#define clist_insert(head, c, next, prev) \
86
+	clist_insert_sublist(head, c, c, next, prev)
87
+
88
+
89
+
90
+/* append at the end of the list (head->prev) */
91
+#define clist_append(head, c, next, prev) \
92
+	clist_append_sublist(head, c, c, next, prev)
93
+
94
+
95
+
96
+/* remove and element */
97
+#define clist_rm(c, next, prev) \
98
+	clist_rm_sublist(c, c, next, prev)
99
+
100
+
101
+
102
+/* iterate on a clist */
103
+#define clist_foreach(head, v, dir) \
104
+	for((v)=(head)->dir; (v)!=(void*)(head); (v)=(v)->dir)
105
+
106
+/* iterate on a clist, safe version (requires an extra bak. var)
107
+ * (it allows removing of the current element) */
108
+#define clist_foreach_safe(head, v, bak,  dir) \
109
+	for((v)=(head)->dir, (bak)=(v)->dir; (v)!=(void*)(head); \
110
+				(v)=(bak), (bak)=(v)->dir)
111
+#endif
... ...
@@ -124,7 +124,6 @@
124 124
 /*used if SH_MEM is defined*/
125 125
 #define SHM_MEM_SIZE 32
126 126
 
127
-#define TIMER_TICK 1
128 127
 
129 128
 /* dimensioning buckets in q_malloc */
130 129
 /* size of the size2bucket table; everything beyond that asks for
131 130
new file mode 100644
... ...
@@ -0,0 +1,174 @@
1
+# $Id$
2
+#
3
+# History:
4
+# --------
5
+# 2005-11-30    created by andrei
6
+
7
+
8
+New timer interface
9
+
10
+
11
+1. Introduction
12
+---------------
13
+
14
+ ser's new timer interface is based on a 3 level hierarchical timing wheel
15
+(see [1]). Most timeouts will go in the first "wheel" (all timeouts < 1<<14 
16
+ ticks, which by default mean 1024 s). Each wheel acts as a circular buffer.
17
+ The big advantage of this scheme is that most of the time you just run all the timer handlers from the current entry in the first wheel (no comparisons necessary). Each 2^14 ticks, all the timers in the second wheel's current entry are redistributed and each 2^23 ticks all the timers in the third wheel's current entry are redistributed.
18
+
19
+ The new timer interfaces allows adding timers dynamically, supports one shot
20
+ and periodic timers, is precise and fast (very low overhead) and supports
21
+"fast" and "slow" timers. For now it uses setitimer to "generate" the ticks and form time to time it re-adjusts them based on the real system time.
22
+
23
+
24
+[1] - G. Varghese, T. Lauck,  Hashed and Hierarchical Timing Wheels: Efficient
25
+      Data Structures for Implementing a Timer Facility, 1996
26
+
27
+
28
+2. include files
29
+-----------------
30
+
31
+All the public functions are defined in timer.h. timer_ticks.h contains
32
+ ticks conversion related macros (ticks to seconds, ms or viceversa).
33
+
34
+
35
+3. Example usage
36
+----------------
37
+
38
+#include "../../timer.h"
39
+#include "../../timer_ticks.h"
40
+
41
+/* simple periodic timer handler */
42
+static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data)
43
+{
44
+	DBG("timer habdler called at %d ticks, foo is %d \n", ticks, *(int*)data);
45
+	return (ticks_t)(-1); /* periodical */
46
+}
47
+
48
+struct timer_ln *t;
49
+int foo;
50
+
51
+t=timer_alloc();
52
+if (t==0)
53
+	goto error;
54
+timer_init(t, timer_handle, &foo, 0);
55
+foo=0;
56
+timer_add(t, MS_TO_TICKS(1500)); /* start it after 1500ms */
57
+/* ....  */
58
+/* remove it and change the period to 2 s */
59
+timer_del(t);
60
+timer_reinit(t); /* without init or reinit timer_add will refuse to re-add it*/
61
+timer_add(t, S_TO_TICKS(2));
62
+/* .... */
63
+/* remove it at the end (optional) */
64
+timer_del(t);
65
+timer_free(t);
66
+
67
+
68
+4. Notes
69
+---------
70
+
71
+4.1 alloc & init
72
+----------------
73
+
74
+To use a timer you need a timer_ln structure. This structure must be stored
75
+ in shared memory.
76
+You can either use timer_alloc() which will return a pointer to a shared memory allocated timer_ln structure or you can "attach" timer_ln as a member to one
77
+ of your structures which is already stored in the shared memory.
78
+
79
+The timer_ln structure must be always initialized. Use the timer_init(...) macro for this. To the timer_init macro takes as parameters a pointer to the timer_ln structure, a pointer to a timer_handler_f function, a void* parameter for this
80
+ function and some timer flags.
81
+Example:
82
+
83
+struct foo{
84
+	int bar;
85
+	struct timer_ln timer;
86
+};
87
+
88
+struct foo* f;
89
+f=shm_malloc(sizeof(struct foo));
90
+
91
+time_init(&f->timer, timer_handle, &f->bar, 0);
92
+
93
+
94
+The timer flags can be either 0 (if it's a "slow" timer) or F_TIMER_FAST if
95
+ this is a "fast" timer.
96
+A "fast" timer is a timer that does very little work in its timer handler (it always exits fast). You should use a "slow" timer if you don't care so much if your timer call is a little bit delayed or if you do  dns lookups, query databases, blocking sends/writes. If you don't know which one to choose, choose "slow".
97
+
98
+
99
+4.2 timer handlers
100
+------------------
101
+
102
+The timer handler can be periodic, one shot or it can change from call to call. It all depends on what value you return from it. If you always return (ticks_t)(-1) or timer_ln->initial_timeout you have a periodic timer. If you return 0 you have an one shot timer (the timer will be removed when your timer handler function exits). For any other value v, your timer will be automatically re-added with the next expire set to v (expressed in ticks).
103
+
104
+4.3 timer_add
105
+-------------
106
+
107
+The timer becomes active after you add it with timer_add. timer_add takes as parameters a pointer to the corresponding timer_ln structure and an expire interval (in ticks, use the macros from timer_ticks.h to convert from s/ms).
108
+The timer must be intialized (with timer_init()) before adding it the first time.
109
+If you want to re-add a deleted timer (timer_del was called on it) or an expired one shot timer (the timer handlers returned 0 on the last run), you have to re-init it first, either by calling timer_reinit(t) or by calling again timer_init(...). If you don't re-initialize the timer, timer_add will refuse to add it and it will return -1. So if timer_add returns error (-1) it means that either you're trying to re-add a running timer or a deleted/expired timer that was not re-initialized.
110
+WARNING: do not initialize/re-initialize a running timer!
111
+
112
+4.4 timer_del
113
+-------------
114
+
115
+To remove a timer from the active timer list call timer_del(struct timer_ln*).
116
+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.
117
+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).
118
+
119
+Race example (using the struct foo defined above):
120
+
121
+/* simple one shot timer handler */
122
+static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data)
123
+{
124
+	
125
+	/* free the mem. */
126
+	shm_free(data);
127
+	return 0;
128
+}
129
+
130
+struct foo* f;
131
+f=shm_malloc(sizeof(struct foo));
132
+
133
+time_init(&f->timer, timer_handle, f, 0);
134
+timer_add(&f->timer, rand());
135
+/* ... */
136
+/* random amount of time spent doing other things */
137
+timer_del(&f->timer); /* if the timer is already expired => f is already
138
+                         deleted => problems */
139
+
140
+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.
141
+
142
+
143
+4.5 Getting the ticks value
144
+----------------------------
145
+
146
+If you need the current ticks value you can get with get_ticks_raw().
147
+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.
148
+
149
+4.6 Conversion
150
+---------------
151
+
152
+To convert between ticks & time and viceversa, include timer_ticks.h and use
153
+one of the following macros:
154
+
155
+MS_TO_TICKS(ms) /* converts from milliseconds to ticks, rounds up */
156
+S_TO_TICKS(s)   /* convert from seconds to ticks */
157
+TICKS_TO_MS(t)  /* converts from ticks to milliseconds, can overflow for
158
+                   very large values (use long long and
159
+                   TICKS_TO_MS((long long)t) to try to avoid it if you know
160
+                   that you'll deal with such large values */
161
+TICKS_TO_S(t)  /* converts from ticks to s, rounded down */
162
+
163
+
164
+4.7 Backward compatibility
165
+--------------------------
166
+
167
+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.
168
+
169
+
170
+
171
+[Todo]:
172
+- SLOW, DRIFT, RESYNC, FREQUENCY
173
+
174
+
... ...
@@ -109,7 +109,7 @@ int err2reason_phrase(
109 109
 
110 110
 		case E_OUT_OF_MEM:
111 111
 		/* dont disclose lack of mem in release mode */
112
-#ifdef DEBUG
112
+#ifdef EXTRA_DEBUG
113 113
 			error_txt="Excuse me I ran out of memory";
114 114
 			*sip_error=500;
115 115
 			break;
... ...
@@ -150,6 +150,9 @@ extern int is_main;
150 150
 /* debugging level for dumping memory status */
151 151
 extern int memlog;
152 152
 
153
+/* debugging level for timer debugging (see -DTIMER_DEBUG) */
154
+extern int timerlog;
155
+
153 156
 /* looking up outbound interface ? */
154 157
 extern int mhomed;
155 158
 
... ...
@@ -119,6 +119,7 @@
119 119
 #include "pt.h"
120 120
 #include "script_cb.h"
121 121
 #include "ut.h"
122
+#include "signals.h"
122 123
 #ifdef USE_TCP
123 124
 #include "poll_types.h"
124 125
 #include "tcp_init.h"
... ...
@@ -255,6 +256,8 @@ int check_via =  0;
255 256
 int syn_branch = 1;
256 257
 /* debugging level for memory stats */
257 258
 int memlog = L_DBG;
259
+/* debugging level for timer debugging */
260
+int timerlog = L_WARN;
258 261
 /* should replies include extensive warnings? by default yes,
259 262
    good for trouble-shooting
260 263
 */
... ...
@@ -424,28 +427,6 @@ static void kill_all_children(int signum)
424 427
 
425 428
 
426 429
 
427
-#ifdef USE_SIGACTION
428
-static void (*set_sig_h(int sig, void (*handler) (int) ))(int)
429
-{
430
-	struct sigaction act;
431
-	struct sigaction old;
432
-	
433
-	memset(&act, 0, sizeof(act));
434
-	act.sa_handler=handler;
435
-	/*
436
-	sigemptyset(&act.sa_mask);
437
-	act.sa_flags=0;
438
-	*/
439
-	LOG(L_CRIT, "setting signal %d to %p\n", sig, handler);
440
-	/* sa_sigaction not set, we use sa_hanlder instead */ 
441
-	return (sigaction (sig, &act, &old)==-1)?SIG_ERR:old.sa_handler;
442
-}
443
-#else
444
-#define set_sig_h signal
445
-#endif
446
-
447
-
448
-
449 430
 /* if this handler is called, a critical timeout has occured while
450 431
  * waiting for the children to finish => we should kill everything and exit */
451 432
 static void sig_alarm_kill(int signo)
... ...
@@ -842,7 +823,7 @@ int main_loop()
842 823
 #ifdef USE_TCP
843 824
 	int sockfd[2];
844 825
 #endif
845
-#ifdef DEBUG
826
+#ifdef EXTRA_DEBUG
846 827
 	int r;
847 828
 #endif
848 829
 
... ...
@@ -884,23 +865,38 @@ int main_loop()
884 865
 		   as new processes are forked (while skipping 0 reserved for main 
885 866
 		*/
886 867
 
887
-		/* we need another process to act as the timer*/
888
-#ifdef USE_TCP
889
-		/* if we are using tcp we always need a timer process,
890
-		 * we cannot count on select timeout to measure time
891
-		 * (it works only on linux)
892
-		 */
893
-		if ((!tcp_disable)||(timer_list))
894
-#else
895
-		if (timer_list)
868
+#ifdef USE_SLOW_TIMER
869
+		/* we need another process to act as the "slow" timer*/
870
+				process_no++;
871
+				if ((pid=fork())<0){
872
+					LOG(L_CRIT,  "ERROR: main_loop: Cannot fork\n");
873
+					goto error;
874
+				}
875
+				if (pid==0){
876
+					/* child */
877
+					pt[process_no].pid=getpid();
878
+					/* timer!*/
879
+					/* process_bit = 0; */
880
+					if (init_child(PROC_TIMER) < 0) {
881
+						LOG(L_ERR, "slow timer: init_child failed\n");
882
+						goto error;
883
+					}
884
+					
885
+					if (arm_slow_timer()<0) goto error;
886
+					slow_timer_main();
887
+				}else{
888
+					pt[process_no].pid=pid; /*should be shared mem anyway*/
889
+					strncpy(pt[process_no].desc, "slow timer", MAX_PT_DESC );
890
+					slow_timer_pid=pid;
891
+					
892
+				}
896 893
 #endif
897
-		{
894
+				/* we need another process to act as the "main" timer*/
898 895
 				process_no++;
899 896
 				if ((pid=fork())<0){
900 897
 					LOG(L_CRIT,  "ERROR: main_loop: Cannot fork\n");
901 898
 					goto error;
902 899
 				}
903
-				
904 900
 				if (pid==0){
905 901
 					/* child */
906 902
 					/* record pid twice to avoid the child using it, before
... ...
@@ -912,15 +908,13 @@ int main_loop()
912 908
 						LOG(L_ERR, "timer: init_child failed\n");
913 909
 						goto error;
914 910
 					}
915
-					for(;;){
916
-						sleep(TIMER_TICK);
917
-						timer_ticker();
918
-					}
911
+					
912
+					if (arm_timer()<0) goto error;
913
+					timer_main();
919 914
 				}else{
920 915
 						pt[process_no].pid=pid; /*should be shared mem anyway*/
921 916
 						strncpy(pt[process_no].desc, "timer", MAX_PT_DESC );
922 917
 				}
923
-		}
924 918
 
925 919
 		/* if configured, start a server for accepting FIFO commands,
926 920
 		 * we need to do it after all the sockets are initialized, to 
... ...
@@ -1101,13 +1095,55 @@ int main_loop()
1101 1095
 	bind_address=0;				/* main proc -> it shouldn't send anything, */
1102 1096
 	
1103 1097
 
1098
+	{
1099
+#ifdef USE_SLOW_TIMER
1104 1100
 #ifdef USE_TCP
1105
-	/* if we are using tcp we always need the timer */
1106
-	if ((!tcp_disable)||(timer_list))
1107
-#else
1108
-	if (timer_list)
1101
+		if (!tcp_disable){
1102
+ 			if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
1103
+				LOG(L_ERR, "ERROR: main_loop: socketpair failed: %s\n",
1104
+					strerror(errno));
1105
+				goto error;
1106
+			}
1107
+		}
1109 1108
 #endif
1110
-	{
1109
+		/* fork again for the "slow" timer process*/
1110
+		process_no++;
1111
+		if ((pid=fork())<0){
1112
+			LOG(L_CRIT, "main_loop: cannot fork \"slow\" timer process\n");
1113
+			goto error;
1114
+		}else if (pid==0){
1115
+			/* child */
1116
+			/* is_main=0; */
1117
+#ifdef USE_TCP
1118
+			if (!tcp_disable){
1119
+				close(sockfd[0]);
1120
+				unix_tcp_sock=sockfd[1];
1121
+			}
1122
+#endif
1123
+			/* record pid twice to avoid the child using it, before
1124
+			 * parent gets a chance to set it*/
1125
+			pt[process_no].pid=getpid();
1126
+			if (init_child(PROC_TIMER) < 0) {
1127
+				LOG(L_ERR, "slow timer: init_child failed\n");
1128
+				goto error;
1129
+			}
1130
+			if (arm_slow_timer()<0) goto error;
1131
+			slow_timer_main();
1132
+		}else{
1133
+			pt[process_no].pid=pid;
1134
+			strncpy(pt[process_no].desc, "slow timer", MAX_PT_DESC );
1135
+			slow_timer_pid=pid;
1136
+#ifdef USE_TCP
1137
+			if(!tcp_disable){
1138
+						close(sockfd[1]);
1139
+						pt[process_no].unix_sock=sockfd[0];
1140
+						pt[process_no].idx=-1; /* this is not a "tcp" process*/
1141
+			}
1142
+#endif
1143
+		}
1144
+#endif /* USE_SLOW_TIMER */
1145
+	
1146
+		/* fork again for the "main" timer process*/
1111 1147
 #ifdef USE_TCP
1112 1148
 		if (!tcp_disable){
1113 1149
  			if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
... ...
@@ -1117,7 +1153,6 @@ int main_loop()
1117 1153
 			}
1118 1154
 		}
1119 1155
 #endif
1120
-		/* fork again for the attendant process*/
1121 1156
 		process_no++;
1122 1157
 		if ((pid=fork())<0){
1123 1158
 			LOG(L_CRIT, "main_loop: cannot fork timer process\n");
... ...
@@ -1138,14 +1173,8 @@ int main_loop()
1138 1173
 				LOG(L_ERR, "timer: init_child failed\n");
1139 1174
 				goto error;
1140 1175
 			}
1141
-			
1142
-			for(;;){
1143
-				/* debug:  instead of doing something useful */
1144
-				/* (placeholder for timers, etc.) */
1145
-				sleep(TIMER_TICK);
1146
-				/* if we received a signal => TIMER_TICK may have not elapsed*/
1147
-				timer_ticker();
1148
-			}
1176
+			if (arm_timer()<0) goto error;
1177
+			timer_main();
1149 1178
 		}else{
1150 1179
 			pt[process_no].pid=pid;
1151 1180
 			strncpy(pt[process_no].desc, "timer", MAX_PT_DESC );
... ...
@@ -1198,11 +1227,14 @@ int main_loop()
1198 1227
 	}
1199 1228
 #endif
1200 1229
 	/*DEBUG- remove it*/
1201
-#ifdef DEBUG
1230
+#ifdef EXTRA_DEBUG
1202 1231
 	fprintf(stderr, "\n% 3d processes (%3d), % 3d children * "
1203 1232
 			"listening addresses + tcp listeners + tls listeners"
1204
-			"+ main + fifo %s\n", process_no+1, process_count(), children_no,
1205
-			(timer_list)?"+ timer":"");
1233
+			"+ main + fifo + timer"
1234
+# ifdef USE_SLOW_TIMER
1235
+			" + slow_timer"
1236
+# endif
1237
+			"\n", process_no+1, process_count(), children_no);
1206 1238
 	for (r=0; r<=process_no; r++){
1207 1239
 		fprintf(stderr, "% 3d   % 5d - %s\n", r, pt[r].pid, pt[r].desc);
1208 1240
 	}
... ...
@@ -74,6 +74,9 @@ inline static int process_count()
74 74
 		/* timer process */
75 75
 		+ 1 /* always, we need it in most cases, and we can't tell here
76 76
 			   & now if we don't need it */
77
+#ifdef USE_SLOW_TIMER
78
+		+ 1 /* slow timer process */
79
+#endif
77 80
 		/* fifo server */
78 81
 		+((fifo==NULL || strlen(fifo)==0) ? 0 : 1 )
79 82
 		/* unixsock server*/
80 83
new file mode 100644
... ...
@@ -0,0 +1,54 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2003 FhG Fokus
5
+ *
6
+  * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/*
28
+ * Handle the signals
29
+ *
30
+ * History:
31
+ * --------
32
+ *  2005-10-05  split from main.c  (andrei)
33
+ */
34
+
35
+#include "signals.h"
36
+#include <signal.h>
37
+
38
+#ifdef USE_SIGACTION
39
+void (*set_sig_h(int sig, void (*handler) (int) ))(int)
40
+{
41
+	struct sigaction act;
42
+	struct sigaction old;
43
+	
44
+	memset(&act, 0, sizeof(act));
45
+	act.sa_handler=handler;
46
+	/*
47
+	sigemptyset(&act.sa_mask);
48
+	act.sa_flags=0;
49
+	*/
50
+	LOG(L_CRIT, "setting signal %d to %p\n", sig, handler);
51
+	/* sa_sigaction not set, we use sa_hanlder instead */ 
52
+	return (sigaction (sig, &act, &old)==-1)?SIG_ERR:old.sa_handler;
53
+}
54
+#endif
0 55
new file mode 100644
... ...
@@ -0,0 +1,46 @@
1
+/*
2
+ * $Id$
3
+ *
4
+ * Copyright (C) 2001-2003 FhG Fokus
5
+ *
6
+ * This file is part of ser, a free SIP server.
7
+ *
8
+ * ser is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * For a license to use the ser software under conditions
14
+ * other than those described here, or to purchase support for this
15
+ * software, please contact iptel.org by e-mail at the following addresses:
16
+ *    info@iptel.org
17
+ *
18
+ * ser is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License 
24
+ * along with this program; if not, write to the Free Software 
25
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
+ */
27
+/*
28
+ * Handle the signals
29
+ *
30
+ * History:
31
+ * --------
32
+ *  2005-10-05  split from main.c  (andrei)
33
+ */
34
+
35
+
36
+#ifndef _signals_h
37
+#define _signals_h
38
+
39
+#ifdef USE_SIGACTION
40
+void (*set_sig_h(int sig, void (*handler) (int) ))(int);
41
+#else
42
+#define set_sig_h signal
43
+#endif
44
+
45
+
46
+#endif
... ...
@@ -28,72 +28,817 @@
28 28
  * --------
29 29
  *  2003-03-19  replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei)
30 30
  *  2003-03-29  cleaning pkg_mallocs introduced (jiri)
31
+ *  2005-07-27  complete re-design/re-implementation (andrei)
31 32
  */
32 33
 
33 34
 
34 35
 #include "timer.h"
36
+#include "timer_funcs.h"
37
+#include "timer_ticks.h"
35 38
 #include "dprint.h"
39
+#include <time.h>     /* gettimeofday */
40
+#include <sys/time.h> /* setitimer, gettimeofday */
41
+#include <signal.h>   /* SIGALRM */
42
+#include <errno.h>
43
+#include <unistd.h> /* pause() */
44
+#include <stdlib.h> /* random, debugging only */
36 45
 #include "error.h"
46
+#include "signals.h"
47
+/*
37 48
 #include "config.h"
49
+*/
50
+#include "globals.h"
38 51
 #include "mem/mem.h"
39 52
 #ifdef SHM_MEM
40 53
 #include "mem/shm_mem.h"
41 54
 #endif
55
+#include "locking.h"
56
+
57
+
58
+
42 59
 
43
-#include <stdlib.h>
60
+/* how often will the timer handler be called (in ticks) */
61
+#define TIMER_HANDLER_INTERVAL	1U
62
+/* how often to try to re-adjust the ticks */
63
+#define TIMER_RESYNC_TICKS	(TIMER_TICKS_HZ*5U)  /* each 5 s */
64
+#define TIMER_MAX_DRIFT	(TIMER_TICKS_HZ/10U) /* if drift > 0.1s adjust */
44 65
 
45 66
 
46
-struct sr_timer* timer_list=0;
47 67
 
48
-static int* jiffies=0;
68
+static ticks_t* ticks=0;
69
+static ticks_t last_ticks; /* last time we adjusted the time */
70
+static ticks_t last_adj_check; /* last time we ran adjust_ticks */
71
+static ticks_t prev_ticks; /* last time we ran the timer, also used as
72
+							  "current" ticks when running the timer for
73
+							  "skipped" ticks */
74
+
75
+static struct timeval last_time;
76
+static struct timeval start_time; /* for debugging */
77
+
78
+static volatile int run_timer=0;
49 79
 static int timer_id=0;
50 80
 
81
+static gen_lock_t* timer_lock=0;
82
+static struct timer_ln* volatile* running_timer=0;/* running timer handler */
83
+
84
+#define LOCK_TIMER_LIST()		lock_get(timer_lock)
85
+#define UNLOCK_TIMER_LIST()		lock_release(timer_lock)
86
+
87
+/* we can get away without atomic_set/atomic_cmp and write barriers because we
88
+ * always call SET_RUNNING and IS_RUNNING while holding the timer lock
89
+ * => it's implicitly atomic and the lock acts as write barrier */
90
+#define SET_RUNNING(t)		(*running_timer=(t))
91
+#define IS_RUNNING(t)		(*running_timer==(t))
92
+#define UNSET_RUNNING()		(*running_timer=0)
93
+
94
+#ifdef USE_SLOW_TIMER
95
+
96
+#define SLOW_TIMER_SIG	SIGUSR2
97
+/* timer flags checks */
98
+#define IS_FAST_TIMER(t)	(t->flags&F_TIMER_FAST)
99
+#define SET_SLOW_LIST(t)	(t->flags|=F_TIMER_ON_SLOW_LIST)
100
+#define RESET_SLOW_LIST(t)	(t->flags&=~F_TIMER_ON_SLOW_LIST)
101
+#define IS_ON_SLOW_LIST(t)	(t->flags&F_TIMER_ON_SLOW_LIST)
102
+
103
+#define SLOW_LISTS_NO	1024U  /* slow lists number, 2^k recommended */
104
+
105
+
106
+static gen_lock_t*  slow_timer_lock; /* slow timer lock */
107
+static struct timer_head* slow_timer_lists; 
108
+static volatile unsigned short* t_idx; /* "main" timer index in slow_lists[] */
109
+static volatile unsigned short* s_idx; /* "slow" timer index in slow_lists[] */
110
+static struct timer_ln* volatile* running_timer2=0; /* timer handler running
111
+													     in the "slow" timer */
112
+static sigset_t slow_timer_sset;
113
+pid_t slow_timer_pid;
114
+#define SET_RUNNING_SLOW(t)		(*running_timer2=(t))
115
+#define IS_RUNNING_SLOW(t)		(*running_timer2==(t))
116
+#define UNSET_RUNNING_SLOW()	(*running_timer2=0)
117
+
118
+#define LOCK_SLOW_TIMER_LIST()		lock_get(slow_timer_lock)
119
+#define UNLOCK_SLOW_TIMER_LIST()	lock_release(slow_timer_lock)
120
+
121
+
122
+#endif
123
+
124
+
125
+struct timer_lists* timer_lst=0;
126
+
127
+void sig_timer(int signo)
128
+{
129
+	(*ticks)++;
130
+	if (( *ticks % TIMER_HANDLER_INTERVAL)==0){
131
+		/* set a flag to run the handler */
132
+		run_timer=1;
133
+	}
134
+}
135
+
136
+
137
+
138
+void destroy_timer()
139
+{
140
+	struct itimerval it;
141
+	
142
+	/* disable timer */
143
+	memset(&it, 0, sizeof(it));
144
+	setitimer(ITIMER_REAL, &it, 0); 
145
+	set_sig_h(SIGALRM, SIG_IGN);
146
+	if (timer_lock){
147
+		lock_destroy(timer_lock);
148
+		lock_dealloc(timer_lock);
149
+		timer_lock=0;
150
+	}
151
+	if (ticks){
152
+#ifdef SHM_MEM
153
+		shm_free(ticks);
154
+#else
155
+		pkg_free(ticks);
156
+#endif
157
+		ticks=0;
158
+	}
159
+	if (timer_lst){
160
+#ifdef SHM_MEM
161
+		shm_free(timer_lst);
162
+#else
163
+		pkg_free(timer_lst);
164
+#endif
165
+		timer_lst=0;
166
+	}
167
+	if (running_timer){
168
+		shm_free((void*)running_timer);
169
+		running_timer=0;
170
+	}
171
+#ifdef USE_SLOW_TIMER
172
+	if (slow_timer_lock){
173
+		lock_destroy(slow_timer_lock);
174
+		lock_dealloc(slow_timer_lock);
175
+		slow_timer_lock=0;
176
+	}
177
+	if (slow_timer_lists){
178
+		shm_free((void*)slow_timer_lists);
179
+		slow_timer_lists=0;
180
+	}
181
+	if (t_idx){
182
+		shm_free((void*)t_idx);
183
+		t_idx=0;
184
+	}
185
+	if (s_idx){
186
+		shm_free((void*)s_idx);
187
+		s_idx=0;
188
+	}
189
+	if(running_timer2){
190
+		shm_free((void*)running_timer2);
191
+		running_timer2=0;
192
+	}
193
+#endif
194
+}
195
+
51 196
 
52 197
 
53 198
 /* ret 0 on success, <0 on error*/
54 199
 int init_timer()
55 200
 {
201
+	int r;
202
+	int ret;
203
+	
204
+	ret=-1;
205
+	
206
+	/* init the locks */
207
+	timer_lock=lock_alloc();
208
+	if (timer_lock==0){
209
+		ret=E_OUT_OF_MEM;
210
+		goto error;
211
+	}
212
+	if (lock_init(timer_lock)==0){
213
+		lock_dealloc(timer_lock);
214
+		timer_lock=0;
215
+		ret=-1;
216
+		goto error;
217
+	}
218
+	/* init the shared structs */
56 219
 #ifdef SHM_MEM
57
-	jiffies=shm_malloc(sizeof(int));
220
+	ticks=shm_malloc(sizeof(ticks_t));
221
+	timer_lst=shm_malloc(sizeof(struct timer_lists));
58 222
 #else
59 223
 	/* in this case get_ticks won't work! */
60 224
 	LOG(L_INFO, "WARNING: no shared memory support compiled in"
61 225
 				" get_ticks won't work\n");
62
-	jiffies=pkg_malloc(sizeof(int));
226
+	ticks=pkg_malloc(sizeof(ticks_t));
227
+	timer_lst=pkg_malloc(sizeof(struct timer_lists));
63 228
 #endif
64
-	if (jiffies==0){
65
-		LOG(L_CRIT, "ERROR: init_timer: could not init jiffies\n");
66
-		return E_OUT_OF_MEM;
229
+	if (ticks==0){
230
+		LOG(L_CRIT, "ERROR: init_timer: out of shared memory (ticks)\n");
231
+		ret=E_OUT_OF_MEM;
232
+		goto error;
67 233
 	}
68
-	*jiffies=0;
234
+	if (timer_lst==0){
235
+		LOG(L_CRIT, "ERROR: init_timer: out of shared memory (timer_lst)\n");
236
+		ret=E_OUT_OF_MEM;
237
+		goto error;
238
+	}
239
+	running_timer=shm_malloc(sizeof(struct timer_ln*));
240
+	if (running_timer==0){
241
+		LOG(L_CRIT, "ERROR: init_timer: out of memory (running_timer)\n");
242
+		ret=E_OUT_OF_MEM;
243
+		goto error;
244
+	}
245
+
246
+	/* initial values */
247
+	memset(timer_lst, 0, sizeof(struct timer_lists));
248
+	*ticks=random(); /* random value for start, for debugging */
249
+	prev_ticks=last_ticks=last_adj_check=*ticks;
250
+	*running_timer=0;
251
+	if (gettimeofday(&start_time, 0)<0){
252
+		LOG(L_ERR, "ERROR: init_timer: gettimeofday failed: %s [%d]\n",
253
+				strerror(errno), errno);
254
+		ret=-1;
255
+		goto error;
256
+	}
257
+	last_time=start_time;
258
+	DBG("init_timer: starting with *ticks=%u\n", (unsigned) *ticks);
259
+	
260
+	/* init timer structures */
261
+	for (r=0; r<H0_ENTRIES; r++)
262
+		_timer_init_list(&timer_lst->h0[r]);
263
+	for (r=0; r<H1_ENTRIES; r++)
264
+		_timer_init_list(&timer_lst->h1[r]);
265
+	for (r=0; r<H2_ENTRIES; r++)
266
+		_timer_init_list(&timer_lst->h2[r]);
267
+	_timer_init_list(&timer_lst->expired);
268
+	
269
+#ifdef USE_SLOW_TIMER
270
+	
271
+	/* init the locks */
272
+	slow_timer_lock=lock_alloc();
273
+	if (slow_timer_lock==0){
274
+		ret=E_OUT_OF_MEM;
275
+		goto error;
276
+	}
277
+	if (lock_init(slow_timer_lock)==0){
278
+		lock_dealloc(slow_timer_lock);
279
+		slow_timer_lock=0;
280
+		ret=-1;
281
+		goto error;
282
+	}
283
+	t_idx=shm_malloc(sizeof(*t_idx));
284
+	s_idx=shm_malloc(sizeof(*s_idx));
285
+	slow_timer_lists=shm_malloc(sizeof(struct timer_head)*SLOW_LISTS_NO);
286
+	running_timer2=shm_malloc(sizeof(struct timer_ln*));
287
+	if ((t_idx==0)||(s_idx==0) || (slow_timer_lists==0) ||(running_timer2==0)){
288
+		LOG(L_ERR, "ERROR: init_timer: out of shared memory (slow)\n");
289
+		ret=E_OUT_OF_MEM;
290
+		goto error;
291
+	}
292
+	*t_idx=*s_idx=0;
293
+	*running_timer2=0;
294
+	for (r=0; r<SLOW_LISTS_NO; r++)
295
+		_timer_init_list(&slow_timer_lists[r]);
296
+	
297
+#endif
298
+	
299
+	DBG("init_timer: timer_list between %p and %p\n",
300
+			&timer_lst->h0[0], &timer_lst->h2[H2_ENTRIES]);
69 301
 	return 0;
302
+error:
303
+	destroy_timer();
304
+	return ret;
70 305
 }
71 306
 
72 307
 
73 308
 
74
-void destroy_timer()
309
+#ifdef USE_SLOW_TIMER
310
+/* arm the "slow" timer ( start it) 
311
+ * returns -1 on error
312
+ * WARNING: use it in the same process as the timer
313
+ *  (the one using pause(); timer_handler()) or
314
+ *  change run_timer to a pointer in shared mem */
315
+int arm_slow_timer()
75 316
 {
76
-	struct sr_timer* t, *foo;
317
+	sigemptyset(&slow_timer_sset);
318
+	sigaddset(&slow_timer_sset, SLOW_TIMER_SIG);
319
+again:
320
+	if (sigprocmask(SIG_BLOCK, &slow_timer_sset, 0)==-1){
321
+		if (errno==EINTR) goto again;
322
+		LOG(L_ERR, "ERROR: arm_slow_timer: sigprocmask failed: %s [%d]}n",
323
+				strerror(errno), errno);
324
+		goto error;
325
+	}
326
+	return 0;
327
+error:
328
+	return -1;
329
+}
330
+#endif
77 331
 
78
-	if (jiffies){
79
-#ifdef SHM_MEM
80
-		shm_free(jiffies); jiffies=0;
332
+
333
+
334
+
335
+/* arm the timer ( start it) 
336
+ * returns -1 on error
337
+ * WARNING: use it in the same process as the timer
338
+ *  (the one using pause(); timer_handler()) or
339
+ *  change run_timer to a pointer in shared mem */
340
+int arm_timer()
341
+{
342
+	struct itimerval it;
343
+	/* init signal generation */
344
+	it.it_interval.tv_sec=0;
345
+	it.it_interval.tv_usec=1000000/TIMER_TICKS_HZ;
346
+	it.it_value=it.it_interval;
347
+	/* install the signal handler */
348
+	if (set_sig_h(SIGALRM, sig_timer) == SIG_ERR ){
349
+		LOG(L_CRIT, "ERROR: init_timer: the SIGALRM signal handler cannot"
350
+					" be installed: %s [%d]\n", strerror(errno), errno);
351
+		return -1;
352
+	}
353
+	if (setitimer(ITIMER_REAL, &it, 0) == -1){
354
+		LOG(L_CRIT, "ERROR: init_timer: setitimer failed: %s [%d]\n",
355
+					strerror(errno), errno);
356
+		return -1;
357
+	}
358
+	if (gettimeofday(&last_time, 0)<0){
359
+		LOG(L_ERR, "ERROR: arm_timer: gettimeofday failed: %s [%d]\n",
360
+				strerror(errno), errno);
361
+		return -1;
362
+	}
363
+	return 0;
364
+}
365
+
366
+
367
+
368
+/* adjust the timer using the "real" time, each TIMER_RESYNC_TICKS, but only
369
+ * if timer drift > TIMER_MAX_DRIFT
370
+ * NOTES: - it will adjust time within  TIMER_MAX_DRIFT from the "real"
371
+ *          elapsed time
372
+ *        - it will never decrease the *ticks, only increase it (monotonic)
373
+ *        - it works ok as long as the adjustment interval < MAX_TICKS_T
374
+ * -- andrei
375
+ */
376
+inline static void adjust_ticks()
377
+{
378
+	struct timeval crt_time;
379
+	long long diff_time;
380
+	ticks_t diff_time_ticks;
381
+	ticks_t diff_ticks_raw;
382
+	s_ticks_t delta;
383
+	
384
+	/* fix ticks if necessary */
385
+	if ((*ticks-last_adj_check)>=(ticks_t)TIMER_RESYNC_TICKS){
386
+		last_adj_check=*ticks;
387
+		if (gettimeofday(&crt_time, 0)<0){
388
+			LOG(L_ERR, "ERROR: adjust_ticks: gettimeofday failed: %s [%d]\n",
389
+				strerror(errno), errno);
390
+			return; /* ignore */
391
+		}
392
+		diff_time=(long long)crt_time.tv_sec*1000000+crt_time.tv_usec-
393
+					((long long) last_time.tv_sec*1000000+last_time.tv_usec);
394
+		if (diff_time<0){
395
+			LOG(L_WARN, "WARNING: time changed backwards %ld ms ignoring...\n",
396
+						(long)(diff_time/1000));
397
+			last_time=crt_time;
398
+			last_ticks=*ticks;
399
+		}else{
400
+			diff_ticks_raw=*ticks-last_ticks;
401
+			diff_time_ticks=(ticks_t)((diff_time*TIMER_TICKS_HZ)/1000000LL);
402
+			delta=(s_ticks_t)(diff_time_ticks-diff_ticks_raw);
403
+			if (delta<-1){
404
+				LOG(L_WARN, "WARNING: our timer runs faster then real-time"
405
+						" (%u ms / %u ticks our time .->"
406
+						 " %ld ms / %u ticks real time)\n", 
407
+						diff_ticks_raw*1000/TIMER_TICKS_HZ, diff_ticks_raw,
408
+						(long)(diff_time/1000), diff_time_ticks);
409
+				last_time=crt_time;
410
+				last_ticks=*ticks;
411
+			}else{
412
+				/* fix the ticks */
413
+				if (delta>(s_ticks_t)TIMER_MAX_DRIFT){
414
+#ifndef TIMER_DEBUG
415
+					if (delta > 2*(s_ticks_t)TIMER_MAX_DRIFT)
416
+#endif
417
+						LOG(L_INFO, "adjusting timer ticks (%lu) with %ld ms"
418
+								" (%ld ticks)\n",
419
+								(unsigned long)*ticks,
420
+							(long)(delta*1000)/TIMER_TICKS_HZ, (long)delta);
421
+					*ticks+=(ticks_t)delta;
422
+				}else{
423
+					/*DBG("incredible, but our timer is in sync with"
424
+							" real time (%lu)\n", (unsigned long)*ticks);
425
+					*/
426
+				}
427
+			}
428
+		}
429
+	}
430
+}
431
+
432
+
433
+
434
+
435
+struct timer_ln* timer_alloc()
436
+{
437
+	return shm_malloc(sizeof(struct timer_ln));
438
+}
439
+
440
+void timer_free(struct timer_ln* t)
441
+{
442
+	shm_free(t);
443
+}
444
+
445
+
446
+/* unsafe (no lock ) timer add function
447
+ * t = current ticks
448
+ * tl must be filled (the intial_timeout and flags must be set)
449
+ * returns -1 on error, 0 on success */
450
+static inline int _timer_add(ticks_t t, struct timer_ln* tl)
451
+{
452
+	ticks_t delta;
453
+
454
+#ifdef USE_SLOW_TIMER
455
+	tl->flags&=~((unsigned short)F_TIMER_ON_SLOW_LIST);
456
+	tl->slow_idx=0;
457
+#endif
458
+	delta=tl->initial_timeout;
459
+	tl->expire=t+delta;
460
+	return _timer_dist_tl(tl, delta);
461
+}
462
+
463
+
464
+
465
+/* "public", safe timer add functions
466
+ * adds a timer at delta ticks from the current time
467
+ * returns -1 on error, 0 on success
468
+ * WARNING: to re-add a deleted or expired timer you must call
469
+ *          timer_reinit(tl) prior to timer_add
470
+ *          The default behaviour allows timer_add to add a timer only if it
471
+ *          has never been added before.*/
472
+#ifdef TIMER_DEBUG
473
+int timer_add_safe(struct timer_ln* tl, ticks_t delta,
474
+					const char* file, const char* func, unsigned line)
81 475
 #else
82
-		pkg_free(jiffies); jiffies=0;
476
+int timer_add_safe(struct timer_ln* tl, ticks_t delta)
83 477
 #endif
478
+{
479
+	int ret;
480
+	
481
+	LOCK_TIMER_LIST();
482
+	if (tl->flags & F_TIMER_ACTIVE){
483
+#ifdef TIMER_DEBUG
484
+		LOG(timerlog, "timer_add called on an active timer %p (%p, %p),"
485
+					" flags %x\n", tl, tl->next, tl->prev, tl->flags);
486
+		LOG(timerlog, "WARN: -timer_add-; called from %s(%s):%d\n",
487
+					func, file, line);
488
+		LOG(timerlog, "WARN: -timer_add-: added %d times"
489
+					", last from: %s(%s):%d, deleted %d times"
490
+					", last from: %s(%s):%d, init %d times, expired %d \n",
491
+					tl->add_calls, tl->add_func, tl->add_file, tl->add_line,
492
+					tl->del_calls, tl->del_func, tl->del_file, tl->del_line,
493
+					tl->init, tl->expires_no);
494
+#else
495
+		DBG("timer_add called on an active timer %p (%p, %p),"
496
+					" flags %x\n", tl, tl->next, tl->prev, tl->flags);
497
+#endif
498
+		ret=-1; /* refusing to add active or non-reinit. timer */
499
+		goto error;
84 500
 	}
501
+	tl->initial_timeout=delta;
502
+	if ((tl->next!=0) || (tl->prev!=0)){
503
+		LOG(L_CRIT, "BUG: timer_add: called with linked timer: %p (%p, %p)\n",
504
+				tl, tl->next, tl->prev);
505
+		ret=-1;
506
+		goto error;
507
+	}
508
+	tl->flags|=F_TIMER_ACTIVE;
509
+#ifdef TIMER_DEBUG
510
+	tl->add_file=file;
511
+	tl->add_func=func;
512
+	tl->add_line=line;
513
+	tl->add_calls++;
514
+#endif
515
+	ret=_timer_add(*ticks, tl);
516
+error:
517
+	UNLOCK_TIMER_LIST();
518
+	return ret;
519
+}
520
+
521
+
85 522
 
86
-	t=timer_list;
87
-	while(t) {
88
-		foo=t->next;
89
-		pkg_free(t);
90
-		t=foo;
523
+/* safe timer delete
524
+ * deletes tl and inits the list pointer to 0
525
+ * WARNING: to be able to reuse a deleted timer you must call
526
+ *          timer_reinit(tl) on it
527
+ * 
528
+ */
529
+#ifdef TIMER_DEBUG
530
+void timer_del_safe(struct timer_ln* tl,
531
+					const char* file, const char* func, unsigned line)
532
+#else
533
+void timer_del_safe(struct timer_ln* tl)
534
+#endif
535
+{
536
+	
537
+again:
538
+	/* quick exit if timer inactive */
539
+	if ( !(tl->flags & F_TIMER_ACTIVE)){
540
+#ifdef TIMER_DEBUG
541
+		LOG(timerlog, "timer_del called on an inactive timer %p (%p, %p),"
542
+					" flags %x\n", tl, tl->next, tl->prev, tl->flags);
543
+		LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
544
+					func, file, line);
545
+		LOG(timerlog, "WARN: -timer_del-: added %d times"
546
+					", last from: %s(%s):%d, deleted %d times"
547
+					", last from: %s(%s):%d, init %d times, expired %d \n",
548
+					tl->add_calls, tl->add_func, tl->add_file, tl->add_line,
549
+					tl->del_calls, tl->del_func, tl->del_file, tl->del_line,
550
+					tl->init, tl->expires_no);
551
+#else
552
+		DBG("timer_del called on an inactive timer %p (%p, %p),"
553
+					" flags %x\n", tl, tl->next, tl->prev, tl->flags);
554
+#endif
555
+		return;
556
+	}
557
+#ifdef USE_SLOW_TIMER
558
+		if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){
559
+			LOCK_SLOW_TIMER_LIST();
560
+			if (!IS_ON_SLOW_LIST(tl) || (tl->slow_idx==*t_idx)){
561
+				UNLOCK_SLOW_TIMER_LIST();
562
+				goto again;
563
+			}
564
+			if (IS_RUNNING_SLOW(tl)){
565
+				UNLOCK_SLOW_TIMER_LIST();
566
+				sched_yield(); /* wait for it to complete */
567
+				goto again;
568
+			}
569
+			if (tl->next!=0){
570
+				_timer_rm_list(tl); /* detach */
571
+				tl->next=tl->prev=0;
572
+#ifdef TIMER_DEBUG
573
+				tl->del_file=file;
574
+				tl->del_func=func;
575
+				tl->del_line=line;
576
+				tl->flags|=F_TIMER_DELETED;
577
+#endif
578
+			}else{
579
+#ifdef TIMER_DEBUG
580
+				LOG(timerlog, "timer_del: (s) timer %p (%p, %p) flags %x "
581
+							"already detached\n",
582
+							tl, tl->next, tl->prev, tl->flags);
583
+				LOG(timerlog, "WARN: -timer_del-: @%d tl=%p "
584
+					"{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), 
585
+					tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
586
+					tl->data, tl->f, tl->flags);
587
+				LOG(timerlog, "WARN: -timer_del-; called from %s(%s):%d\n",
588
+						func, file, line);
589
+				LOG(timerlog, "WARN: -timer_del-: added %d times"
590
+						", last from: %s(%s):%d, deleted %d times"
591
+						", last from: %s(%s):%d, init %d times, expired %d \n",
592
+						tl->add_calls,
593
+						tl->add_func, tl->add_file, tl->add_line,
594
+						tl->del_calls,
595
+						tl->del_func, tl->del_file, tl->del_line,
596
+						tl->init, tl->expires_no);
597
+#else
598
+				DBG("timer_del: (s) timer %p (%p, %p) flags %x "
599
+							"already detached\n",
600
+							tl, tl->next, tl->prev, tl->flags);
601
+#endif
602
+			}
603
+			UNLOCK_SLOW_TIMER_LIST();
604
+		}else{
605
+#endif
606
+			LOCK_TIMER_LIST();
607
+#ifdef USE_SLOW_TIMER
608
+			if (IS_ON_SLOW_LIST(tl) && (tl->slow_idx!=*t_idx)){
609
+				UNLOCK_TIMER_LIST();
610
+				goto again;
611
+			}
612
+#endif
613
+			if (IS_RUNNING(tl)){
614
+				UNLOCK_TIMER_LIST();
615
+				sched_yield(); /* wait for it to complete */
616
+				goto again;
617
+			}
618
+			if ((tl->next!=0)&&(tl->prev!=0)){
619
+				_timer_rm_list(tl); /* detach */
620
+				tl->next=tl->prev=0;
621
+#ifdef TIMER_DEBUG
622
+				tl->del_file=file;
623
+				tl->del_func=func;
624
+				tl->del_line=line;
625
+				tl->flags|=F_TIMER_DELETED;
626
+#endif
627
+			}else{
628
+#ifdef TIMER_DEBUG
629
+				LOG(timerlog, "timer_del: (f) timer %p (%p, %p) flags %x "
630
+							"already detached\n",
631
+							tl, tl->next, tl->prev, tl->flags);
632
+				LOG(timerlog, "WARN: -timer_del-: @%d tl=%p "
633
+					"{ %p, %p, %d, %d, %p, %p, %04x, -}\n", get_ticks_raw(), 
634
+					tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
635
+					tl->data, tl->f, tl->flags);
636
+				LOG(timerlog, "WANT: -timer_del-; called from %s(%s):%d\n",
637
+						func, file, line);
638
+				LOG(timerlog, "WARN: -timer_del-: added %d times"
639
+						", last from: %s(%s):%d, deleted %d times"
640
+						", last from: %s(%s):%d, init %d times, expired %d \n",
641
+						tl->add_calls,
642
+						tl->add_func, tl->add_file, tl->add_line,
643
+						tl->del_calls,
644
+						tl->del_func, tl->del_file, tl->del_line,
645
+						tl->init, tl->expires_no);
646
+#else
647
+				DBG("timer_del: (f) timer %p (%p, %p) flags %x "
648
+							"already detached\n",
649
+							tl, tl->next, tl->prev, tl->flags);
650
+#endif
651
+			}
652
+			UNLOCK_TIMER_LIST();
653
+#ifdef USE_SLOW_TIMER
654
+		}
655
+#endif
656
+}
657
+
658
+
659
+
660
+/* called from timer_handle, must be called with the timer lock held */
661
+inline static void timer_list_expire(ticks_t t, struct timer_head* h
662
+#ifdef USE_SLOW_TIMER
663
+										, struct timer_head* slow_l,
664
+										slow_idx_t slow_mark
665
+#endif
666
+																	)
667
+{
668
+	struct timer_ln * tl;
669
+	ticks_t ret;
670
+#ifdef TIMER_DEBUG
671
+	struct timer_ln* first;
672
+	int i=0;
673
+	
674
+	first=h->next;
675
+#endif
676
+	
677
+	/*DBG("timer_list_expire @ ticks = %lu, list =%p\n",
678
+			(unsigned long) *ticks, h);
679
+	*/
680
+	while(h->next!=(struct timer_ln*)h){
681
+		tl=h->next;
682
+#ifdef TIMER_DEBUG /* FIXME: replace w/ EXTRA_DEBUG */
683
+		if (tl==0){
684
+			LOG(L_CRIT, "BUG: timer_list_expire: tl=%p, h=%p {%p, %p}\n",
685
+					tl, h, h->next, h->prev);
686
+			abort();
687
+		}else if((tl->next==0) || (tl->prev==0)){
688
+			LOG(L_CRIT, "BUG: timer_list_expire: @%d tl=%p "
689
+					"{ %p, %p, %d, %d, %p, %p, %04x, -},"
690
+					" h=%p {%p, %p}\n", t, 
691
+					tl,  tl->next, tl->prev, tl->expire, tl->initial_timeout,
692
+					tl->data, tl->f, tl->flags, 
693
+					h, h->next, h->prev);
694
+			LOG(L_CRIT, "BUG: -timer_list_expire-: cycle %d, first %p,"
695
+						"running %p\n", i, first, *running_timer);
696
+			LOG(L_CRIT, "BUG: -timer_list_expire-: added %d times"
697
+						", last from: %s(%s):%d, deleted %d times"
698
+						", last from: %s(%s):%d, init %d times, expired %d \n",
699
+						tl->add_calls,
700
+						tl->add_func, tl->add_file, tl->add_line,
701
+						tl->del_calls,
702
+						tl->del_func, tl->del_file, tl->del_line,
703
+						tl->init, tl->expires_no);
704
+			abort();
705
+		}
706
+		i++;
707
+#endif
708
+		_timer_rm_list(tl); /* detach */
709
+#ifdef USE_SLOW_TIMER
710
+		if (IS_FAST_TIMER(tl)){
711
+#endif
712
+		/* if fast timer */
713
+			SET_RUNNING(tl);
714
+			tl->next=tl->prev=0; /* debugging */
715
+#ifdef TIMER_DEBUG
716
+			tl->expires_no++;
717
+#endif
718
+			UNLOCK_TIMER_LIST(); /* acts also as write barrier */ 
719
+				ret=tl->f(t, tl, tl->data);
720
+				if (ret==0){
721
+					UNSET_RUNNING();
722
+					LOCK_TIMER_LIST();
723
+				}else{
724
+					/* not one-shot, re-add it */
725
+					LOCK_TIMER_LIST();
726
+					if (ret!=(ticks_t)-1) /* ! periodic */
727
+						tl->initial_timeout=ret;
728
+					_timer_add(t, tl);
729
+					UNSET_RUNNING();
730
+				}
731
+#ifdef USE_SLOW_TIMER
732
+		}else{
733
+			/* slow timer */
734
+			SET_SLOW_LIST(tl);
735
+			tl->slow_idx=slow_mark; /* current index */
736
+			/* overflow check in timer_handler*/
737
+			_timer_add_list(slow_l, tl);
738
+			
739
+		}
740
+#endif
91 741
 	}
92 742
 }
93 743
 
94 744
 
95 745
 
96
-/*register a periodic timer;
746
+/* "main" timer routine
747
+ * WARNING: it should never be called twice for the same *ticks value
748
+ * (it could cause too fast expires for long timers), *ticks must be also
749
+ *  always increasing */
750
+static void timer_handler()
751
+{