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 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * circular list maintenance macros
4
+ *
5
+ * Copyright (C) 2005 iptelorg GmbH
6
+ *
7
+ * This file is part of ser, a free SIP server.
8
+ *
9
+ * ser is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * For a license to use the ser software under conditions
15
+ * other than those described here, or to purchase support for this
16
+ * software, please contact iptel.org by e-mail at the following addresses:
17
+ *    info@iptel.org
18
+ *
19
+ * ser is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
+ * GNU General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License 
25
+ * along with this program; if not, write to the Free Software 
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
+ */
28
+
29
+/* History:
30
+ * --------
31
+ *  2005-08-08  created by andrei
32
+ */
33
+
34
+#ifndef _clist_h
35
+#define _clist_h
36
+
37
+/* circular list */
38
+#define clist_init(c, next, prev) \
39
+	do{ \
40
+		(c)->next=(void*)(c); \
41
+		(c)->prev=(void*)(c); \
42
+	} while(0)
43
+
44
+
45
+
46
+/* adds an entire sublist { s,e } (including s & e )
47
+ * after head
48
+ */
49
+#define clist_insert_sublist(head, s, e, next, prev) \
50
+	do{ \
51
+		(s)->prev=(head); \
52
+		(e)->next=(head)->next; \
53
+		(e)->next->prev=(e); \
54
+		(head)->next=s;   \
55
+	}while(0)
56
+
57
+
58
+
59
+/* appends an entire sublist { s,e } (including s & e )
60
+ * at the end of the list
61
+ */
62
+#define clist_append_sublist(head, s, e, next, prev) \
63
+	do{ \
64
+		(s)->prev=(head)->prev; \
65
+		(e)->next=(void*)(head); \
66
+		(s)->prev->next=(s); \
67
+		(head)->prev=(e);   \
68
+	}while(0)
69
+
70
+
71
+
72
+/* remove sublist { s,e } (including s & e )
73
+ * always, if start is the beginning of the list use
74
+ * clist_rm_sublist(head->next, e, next, prev ) */
75
+#define clist_rm_sublist(s, e, next, prev) \
76
+	do{\
77
+		(s)->prev->next=(e)->next;  \
78
+		(e)->next->prev=(s)->prev ; \
79
+	}while(0)
80
+
81
+
82
+
83
+/* insert after (head) */
84
+#define clist_insert(head, c, next, prev) \
85
+	clist_insert_sublist(head, c, c, next, prev)
86
+
87
+
88
+
89
+/* append at the end of the list (head->prev) */
90
+#define clist_append(head, c, next, prev) \
91
+	clist_append_sublist(head, c, c, next, prev)
92
+
93
+
94
+
95
+/* remove and element */
96
+#define clist_rm(c, next, prev) \
97
+	clist_rm_sublist(c, c, next, prev)
98
+
99
+
100
+
101
+/* iterate on a clist */
102
+#define clist_foreach(head, v, dir) \
103
+	for((v)=(head)->dir; (v)!=(void*)(head); (v)=(v)->dir)
104
+
105
+/* iterate on a clist, safe version (requires an extra bak. var)
106
+ * (it allows removing of the current element) */
107
+#define clist_foreach_safe(head, v, bak,  dir) \
108
+	for((v)=(head)->dir, (bak)=(v)->dir; (v)!=(void*)(head); \
109
+				(v)=(bak), (bak)=(v)->dir)
110
+#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 @@
0
+# $Id$
1
+#
2
+# History:
3
+# --------
4
+# 2005-11-30    created by andrei
5
+
6
+
7
+New timer interface
8
+
9
+
10
+1. Introduction
11
+---------------
12
+
13
+ ser's new timer interface is based on a 3 level hierarchical timing wheel
14
+(see [1]). Most timeouts will go in the first "wheel" (all timeouts < 1<<14 
15
+ ticks, which by default mean 1024 s). Each wheel acts as a circular buffer.
16
+ 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.
17
+
18
+ The new timer interfaces allows adding timers dynamically, supports one shot
19
+ and periodic timers, is precise and fast (very low overhead) and supports
20
+"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.
21
+
22
+
23
+[1] - G. Varghese, T. Lauck,  Hashed and Hierarchical Timing Wheels: Efficient
24
+      Data Structures for Implementing a Timer Facility, 1996
25
+
26
+
27
+2. include files
28
+-----------------
29
+
30
+All the public functions are defined in timer.h. timer_ticks.h contains
31
+ ticks conversion related macros (ticks to seconds, ms or viceversa).
32
+
33
+
34
+3. Example usage
35
+----------------
36
+
37
+#include "../../timer.h"
38
+#include "../../timer_ticks.h"
39
+
40
+/* simple periodic timer handler */
41
+static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data)
42
+{
43
+	DBG("timer habdler called at %d ticks, foo is %d \n", ticks, *(int*)data);
44
+	return (ticks_t)(-1); /* periodical */
45
+}
46
+
47
+struct timer_ln *t;
48
+int foo;
49
+
50
+t=timer_alloc();
51
+if (t==0)
52
+	goto error;
53
+timer_init(t, timer_handle, &foo, 0);
54
+foo=0;
55
+timer_add(t, MS_TO_TICKS(1500)); /* start it after 1500ms */
56
+/* ....  */
57
+/* remove it and change the period to 2 s */
58
+timer_del(t);
59
+timer_reinit(t); /* without init or reinit timer_add will refuse to re-add it*/
60
+timer_add(t, S_TO_TICKS(2));
61
+/* .... */
62
+/* remove it at the end (optional) */
63
+timer_del(t);
64
+timer_free(t);
65
+
66
+
67
+4. Notes
68
+---------
69
+
70
+4.1 alloc & init
71
+----------------
72
+
73
+To use a timer you need a timer_ln structure. This structure must be stored
74
+ in shared memory.
75
+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
76
+ of your structures which is already stored in the shared memory.
77
+
78
+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
79
+ function and some timer flags.
80
+Example:
81
+
82
+struct foo{
83
+	int bar;
84
+	struct timer_ln timer;
85
+};
86
+
87
+struct foo* f;
88
+f=shm_malloc(sizeof(struct foo));
89
+
90
+time_init(&f->timer, timer_handle, &f->bar, 0);
91
+
92
+
93
+The timer flags can be either 0 (if it's a "slow" timer) or F_TIMER_FAST if
94
+ this is a "fast" timer.
95
+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".
96
+
97
+
98
+4.2 timer handlers
99
+------------------
100
+
101
+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).
102
+
103
+4.3 timer_add
104
+-------------
105
+
106
+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).
107
+The timer must be intialized (with timer_init()) before adding it the first time.
108
+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.
109
+WARNING: do not initialize/re-initialize a running timer!
110
+
111
+4.4 timer_del
112
+-------------
113
+
114
+To remove a timer from the active timer list call timer_del(struct timer_ln*).
115
+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.
116
+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).
117
+
118
+Race example (using the struct foo defined above):
119
+
120
+/* simple one shot timer handler */
121
+static ticks_t timer_h(ticks_t ticks, struct timer_ln* tl, void* data)
122
+{
123
+	
124
+	/* free the mem. */
125
+	shm_free(data);
126
+	return 0;
127
+}
128
+
129
+struct foo* f;
130
+f=shm_malloc(sizeof(struct foo));
131
+
132
+time_init(&f->timer, timer_handle, f, 0);
133
+timer_add(&f->timer, rand());
134
+/* ... */
135
+/* random amount of time spent doing other things */
136
+timer_del(&f->timer); /* if the timer is already expired => f is already
137
+                         deleted => problems */
138
+
139
+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.
140
+
141
+
142
+4.5 Getting the ticks value
143
+----------------------------
144
+
145
+If you need the current ticks value you can get with get_ticks_raw().
146
+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.
147
+
148
+4.6 Conversion
149
+---------------
150
+
151
+To convert between ticks & time and viceversa, include timer_ticks.h and use
152
+one of the following macros:
153
+
154
+MS_TO_TICKS(ms) /* converts from milliseconds to ticks, rounds up */
155
+S_TO_TICKS(s)   /* convert from seconds to ticks */
156
+TICKS_TO_MS(t)  /* converts from ticks to milliseconds, can overflow for
157
+                   very large values (use long long and
158
+                   TICKS_TO_MS((long long)t) to try to avoid it if you know
159
+                   that you'll deal with such large values */
160
+TICKS_TO_S(t)  /* converts from ticks to s, rounded down */
161
+
162
+
163
+4.7 Backward compatibility
164
+--------------------------
165
+
166
+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.
167
+
168
+
169
+
170
+[Todo]:
171
+- SLOW, DRIFT, RESYNC, FREQUENCY
172
+
173
+
... ...
@@ -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 255
 int syn_branch = 1;
256 256
 /* debugging level for memory stats */
257 257
 int memlog = L_DBG;
258
+/* debugging level for timer debugging */
259
+int timerlog = L_WARN;
258 260
 /* should replies include extensive warnings? by default yes,
259 261
    good for trouble-shooting
260 262
 */
... ...
@@ -424,28 +427,6 @@ static void kill_all_children(int signum)
424 424
 
425 425
 
426 426
 
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 427
 /* if this handler is called, a critical timeout has occured while
450 428
  * waiting for the children to finish => we should kill everything and exit */
451 429
 static void sig_alarm_kill(int signo)
... ...
@@ -842,7 +823,7 @@ int main_loop()
842 842
 #ifdef USE_TCP
843 843
 	int sockfd[2];
844 844
 #endif
845
-#ifdef DEBUG
845
+#ifdef EXTRA_DEBUG
846 846
 	int r;
847 847
 #endif
848 848
 
... ...
@@ -884,23 +865,38 @@ int main_loop()
884 884
 		   as new processes are forked (while skipping 0 reserved for main 
885 885
 		*/
886 886
 
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)
887
+#ifdef USE_SLOW_TIMER
888
+		/* we need another process to act as the "slow" timer*/
889
+				process_no++;
890
+				if ((pid=fork())<0){
891
+					LOG(L_CRIT,  "ERROR: main_loop: Cannot fork\n");
892
+					goto error;
893
+				}
894
+				if (pid==0){
895
+					/* child */
896
+					pt[process_no].pid=getpid();
897
+					/* timer!*/
898
+					/* process_bit = 0; */
899
+					if (init_child(PROC_TIMER) < 0) {
900
+						LOG(L_ERR, "slow timer: init_child failed\n");
901
+						goto error;
902
+					}
903
+					
904
+					if (arm_slow_timer()<0) goto error;
905
+					slow_timer_main();
906
+				}else{
907
+					pt[process_no].pid=pid; /*should be shared mem anyway*/
908
+					strncpy(pt[process_no].desc, "slow timer", MAX_PT_DESC );
909
+					slow_timer_pid=pid;
910
+					
911
+				}
896 912
 #endif
897
-		{
913
+				/* we need another process to act as the "main" timer*/
898 914
 				process_no++;
899 915
 				if ((pid=fork())<0){
900 916
 					LOG(L_CRIT,  "ERROR: main_loop: Cannot fork\n");
901 917
 					goto error;
902 918
 				}
903
-				
904 919
 				if (pid==0){
905 920
 					/* child */
906 921
 					/* record pid twice to avoid the child using it, before
... ...
@@ -912,15 +908,13 @@ int main_loop()
912 912
 						LOG(L_ERR, "timer: init_child failed\n");
913 913
 						goto error;
914 914
 					}
915
-					for(;;){
916
-						sleep(TIMER_TICK);
917
-						timer_ticker();
918
-					}
915
+					
916
+					if (arm_timer()<0) goto error;
917
+					timer_main();
919 918
 				}else{
920 919
 						pt[process_no].pid=pid; /*should be shared mem anyway*/
921 920
 						strncpy(pt[process_no].desc, "timer", MAX_PT_DESC );
922 921
 				}
923
-		}
924 922
 
925 923
 		/* if configured, start a server for accepting FIFO commands,
926 924
 		 * we need to do it after all the sockets are initialized, to 
... ...
@@ -1101,13 +1095,55 @@ int main_loop()
1101 1101
 	bind_address=0;				/* main proc -> it shouldn't send anything, */
1102 1102
 	
1103 1103
 
1104
+	{
1105
+#ifdef USE_SLOW_TIMER
1104 1106
 #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)
1107
+		if (!tcp_disable){
1108
+ 			if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
1109
+				LOG(L_ERR, "ERROR: main_loop: socketpair failed: %s\n",
1110
+					strerror(errno));
1111
+				goto error;
1112
+			}
1113
+		}
1109 1114
 #endif
1110
-	{
1115
+		/* fork again for the "slow" timer process*/
1116
+		process_no++;
1117
+		if ((pid=fork())<0){
1118
+			LOG(L_CRIT, "main_loop: cannot fork \"slow\" timer process\n");
1119
+			goto error;
1120
+		}else if (pid==0){
1121
+			/* child */
1122
+			/* is_main=0; */
1123
+#ifdef USE_TCP
1124
+			if (!tcp_disable){
1125
+				close(sockfd[0]);
1126
+				unix_tcp_sock=sockfd[1];
1127
+			}
1128
+#endif
1129
+			/* record pid twice to avoid the child using it, before
1130
+			 * parent gets a chance to set it*/
1131
+			pt[process_no].pid=getpid();
1132
+			if (init_child(PROC_TIMER) < 0) {
1133
+				LOG(L_ERR, "slow timer: init_child failed\n");
1134
+				goto error;
1135
+			}
1136
+			if (arm_slow_timer()<0) goto error;
1137
+			slow_timer_main();
1138
+		}else{
1139
+			pt[process_no].pid=pid;
1140
+			strncpy(pt[process_no].desc, "slow timer", MAX_PT_DESC );
1141
+			slow_timer_pid=pid;
1142
+#ifdef USE_TCP
1143
+			if(!tcp_disable){
1144
+						close(sockfd[1]);
1145
+						pt[process_no].unix_sock=sockfd[0];
1146
+						pt[process_no].idx=-1; /* this is not a "tcp" process*/
1147
+			}
1148
+#endif
1149
+		}
1150
+#endif /* USE_SLOW_TIMER */
1151
+	
1152
+		/* fork again for the "main" timer process*/
1111 1153
 #ifdef USE_TCP
1112 1154
 		if (!tcp_disable){
1113 1155
  			if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd)<0){
... ...
@@ -1117,7 +1153,6 @@ int main_loop()
1117 1117
 			}
1118 1118
 		}
1119 1119
 #endif
1120
-		/* fork again for the attendant process*/
1121 1120
 		process_no++;
1122 1121
 		if ((pid=fork())<0){
1123 1122
 			LOG(L_CRIT, "main_loop: cannot fork timer process\n");
... ...
@@ -1138,14 +1173,8 @@ int main_loop()
1138 1138
 				LOG(L_ERR, "timer: init_child failed\n");
1139 1139
 				goto error;
1140 1140
 			}
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
-			}
1141
+			if (arm_timer()<0) goto error;
1142
+			timer_main();
1149 1143
 		}else{
1150 1144
 			pt[process_no].pid=pid;
1151 1145
 			strncpy(pt[process_no].desc, "timer", MAX_PT_DESC );
... ...
@@ -1198,11 +1227,14 @@ int main_loop()
1198 1198
 	}
1199 1199
 #endif
1200 1200
 	/*DEBUG- remove it*/
1201
-#ifdef DEBUG
1201
+#ifdef EXTRA_DEBUG
1202 1202
 	fprintf(stderr, "\n% 3d processes (%3d), % 3d children * "
1203 1203
 			"listening addresses + tcp listeners + tls listeners"
1204
-			"+ main + fifo %s\n", process_no+1, process_count(), children_no,
1205
-			(timer_list)?"+ timer":"");
1204
+			"+ main + fifo + timer"
1205
+# ifdef USE_SLOW_TIMER
1206
+			" + slow_timer"
1207
+# endif
1208
+			"\n", process_no+1, process_count(), children_no);
1206 1209
 	for (r=0; r<=process_no; r++){
1207 1210
 		fprintf(stderr, "% 3d   % 5d - %s\n", r, pt[r].pid, pt[r].desc);
1208 1211
 	}
... ...
@@ -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 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2001-2003 FhG Fokus
4
+ *
5
+  * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License 
23
+ * along with this program; if not, write to the Free Software 
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ */
26
+/*
27
+ * Handle the signals
28
+ *
29
+ * History:
30
+ * --------
31
+ *  2005-10-05  split from main.c  (andrei)
32
+ */
33
+
34
+#include "signals.h"
35
+#include <signal.h>
36
+
37
+#ifdef USE_SIGACTION
38
+void (*set_sig_h(int sig, void (*handler) (int) ))(int)
39
+{
40
+	struct sigaction act;
41
+	struct sigaction old;
42
+	
43
+	memset(&act, 0, sizeof(act));
44
+	act.sa_handler=handler;
45
+	/*
46
+	sigemptyset(&act.sa_mask);
47
+	act.sa_flags=0;
48
+	*/
49
+	LOG(L_CRIT, "setting signal %d to %p\n", sig, handler);
50
+	/* sa_sigaction not set, we use sa_hanlder instead */ 
51
+	return (sigaction (sig, &act, &old)==-1)?SIG_ERR:old.sa_handler;
52
+}
53
+#endif
0 54
new file mode 100644
... ...
@@ -0,0 +1,46 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2001-2003 FhG Fokus
4
+ *
5
+ * This file is part of ser, a free SIP server.
6
+ *
7
+ * ser is free software; you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation; either version 2 of the License, or
10
+ * (at your option) any later version
11
+ *
12
+ * For a license to use the ser software under conditions
13
+ * other than those described here, or to purchase support for this
14
+ * software, please contact iptel.org by e-mail at the following addresses:
15
+ *    info@iptel.org
16
+ *
17
+ * ser is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License 
23
+ * along with this program; if not, write to the Free Software 
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
+ */
26
+/*
27
+ * Handle the signals
28
+ *
29
+ * History:
30
+ * --------
31
+ *  2005-10-05  split from main.c  (andrei)
32
+ */
33
+
34
+
35
+#ifndef _signals_h
36
+#define _signals_h
37
+
38
+#ifdef USE_SIGACTION
39
+void (*set_sig_h(int sig, void (*handler) (int) ))(int);
40
+#else
41
+#define set_sig_h signal
42
+#endif
43
+
44
+
45
+#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
+{
752
+	ticks_t saved_ticks;