1 /*
2 * Copyright (C) 2009 iptelorg GmbH
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /**
18 * @file
19 * @brief Kamailio core :: timer - separate process timers
20 *
21 * (unrelated to the main fast and slow timers)
22 *
23 * @ingroup core
24 * Module: @ref core
25 */
26
27 #include "timer_proc.h"
28 #include "cfg/cfg_struct.h"
29 #include "pt.h"
30 #include "ut.h"
31 #include "mem/shm_mem.h"
32
33 #include <unistd.h>
34
35
36 /**
37 * \brief update internal counters for running new basic sec. timers
38 * @param timers number of basic timer processes
39 * @return 0 on success; -1 on error
40 */
register_basic_timers(int timers)41 int register_basic_timers(int timers)
42 {
43 if(register_procs(timers)<0)
44 return -1;
45 cfg_register_child(timers);
46 return 0;
47 }
48
49 /**
50 * \brief Forks a separate simple sleep() periodic timer
51 *
52 * Forks a very basic periodic timer process, that just sleep()s for
53 * the specified interval and then calls the timer function.
54 * The new "basic timer" process execution start immediately, the sleep()
55 * is called first (so the first call to the timer function will happen
56 * \<interval\> seconds after the call to fork_basic_timer)
57 * @param child_id @see fork_process()
58 * @param desc @see fork_process()
59 * @param make_sock @see fork_process()
60 * @param f timer function/callback
61 * @param param parameter passed to the timer function
62 * @param interval interval in seconds.
63 * @return pid of the new process on success, -1 on error
64 * (doesn't return anything in the child process)
65 */
fork_basic_timer(int child_id,char * desc,int make_sock,timer_function * f,void * param,int interval)66 int fork_basic_timer(int child_id, char* desc, int make_sock,
67 timer_function* f, void* param, int interval)
68 {
69 int pid;
70
71 pid=fork_process(child_id, desc, make_sock);
72 if (pid<0) return -1;
73 if (pid==0){
74 /* child */
75 if (cfg_child_init()) return -1;
76 for(;;){
77 sleep(interval);
78 cfg_update();
79 f(get_ticks(), param); /* ticks in s for compatibility with old
80 * timers */
81 }
82 }
83 /* parent */
84 return pid;
85 }
86
fork_basic_timer_w(int child_id,char * desc,int make_sock,timer_function_w * f,int worker,void * param,int interval)87 int fork_basic_timer_w(int child_id, char* desc, int make_sock,
88 timer_function_w* f, int worker, void* param, int interval)
89 {
90 int pid;
91
92 pid=fork_process(child_id, desc, make_sock);
93 if (pid<0) return -1;
94 if (pid==0){
95 /* child */
96 if (cfg_child_init()) return -1;
97 for(;;){
98 sleep(interval);
99 cfg_update();
100 f(get_ticks(), worker, param); /* ticks in s for compatibility with old
101 * timers */
102 }
103 }
104 /* parent */
105 return pid;
106 }
107
108 /**
109 * \brief Forks a separate simple microsecond-sleep() periodic timer
110 *
111 * Forks a very basic periodic timer process, that just us-sleep()s for
112 * the specified interval and then calls the timer function.
113 * The new "basic timer" process execution start immediately, the us-sleep()
114 * is called first (so the first call to the timer function will happen
115 * \<interval\> microseconds after the call to fork_basic_utimer)
116 * @param child_id @see fork_process()
117 * @param desc @see fork_process()
118 * @param make_sock @see fork_process()
119 * @param f timer function/callback
120 * @param param parameter passed to the timer function
121 * @param uinterval interval in micro-seconds.
122 * @return pid of the new process on success, -1 on error
123 * (doesn't return anything in the child process)
124 */
fork_basic_utimer(int child_id,char * desc,int make_sock,utimer_function * f,void * param,int uinterval)125 int fork_basic_utimer(int child_id, char* desc, int make_sock,
126 utimer_function* f, void* param, int uinterval)
127 {
128 int pid;
129 ticks_t ts;
130
131 pid=fork_process(child_id, desc, make_sock);
132 if (pid<0) return -1;
133 if (pid==0){
134 /* child */
135 if (cfg_child_init()) return -1;
136 for(;;){
137 sleep_us(uinterval);
138 cfg_update();
139 ts = get_ticks_raw();
140 f(TICKS_TO_MS(ts), param); /* ticks in mili-seconds */
141 }
142 }
143 /* parent */
144 return pid;
145 }
146
fork_basic_utimer_w(int child_id,char * desc,int make_sock,utimer_function_w * f,int worker,void * param,int uinterval)147 int fork_basic_utimer_w(int child_id, char* desc, int make_sock,
148 utimer_function_w* f, int worker, void* param, int uinterval)
149 {
150 int pid;
151 ticks_t ts;
152
153 pid=fork_process(child_id, desc, make_sock);
154 if (pid<0) return -1;
155 if (pid==0){
156 /* child */
157 if (cfg_child_init()) return -1;
158 for(;;){
159 sleep_us(uinterval);
160 cfg_update();
161 ts = get_ticks_raw();
162 f(TICKS_TO_MS(ts), worker, param); /* ticks in mili-seconds */
163 }
164 }
165 /* parent */
166 return pid;
167 }
168
169
170 /**
171 * \brief Forks a timer process based on the local timer
172 *
173 * Forks a separate timer process running a local_timer.h type of timer
174 * A pointer to the local_timer handle (allocated in shared memory) is
175 * returned in lt_h. It can be used to add/delete more timers at runtime
176 * (via local_timer_add()/local_timer_del() a.s.o).
177 * If timers are added from separate processes, some form of locking must be
178 * used (all the calls to local_timer* must be enclosed by locks if it
179 * cannot be guaranteed that they cannot execute in the same time)
180 * The timer "engine" must be run manually from the child process. For
181 * example a very simple local timer process that just runs a single
182 * periodic timer can be started in the following way:
183 * struct local_timer* lt_h;
184 *
185 * pid=fork_local_timer_process(...., <_h);
186 * if (pid==0){
187 * timer_init(&my_timer, my_timer_f, 0, 0);
188 * local_timer_add(<_h, &my_timer, S_TO_TICKS(10), get_ticks_raw());
189 * while(1) { sleep(1); local_timer_run(lt, get_ticks_raw()); }
190 * }
191 *
192 * @param child_id @see fork_process()
193 * @param desc @see fork_process()
194 * @param make_sock @see fork_process()
195 * @param lt_h local_timer handler
196 * @return pid to the parent, 0 to the child, -1 if error.
197 */
fork_local_timer_process(int child_id,char * desc,int make_sock,struct local_timer ** lt_h)198 int fork_local_timer_process(int child_id, char* desc, int make_sock,
199 struct local_timer** lt_h)
200 {
201 int pid;
202 struct local_timer* lt;
203
204 lt=shm_malloc(sizeof(*lt));
205 if (lt==0) goto error;
206 if (init_local_timer(lt, get_ticks_raw())<0) goto error;
207 pid=fork_process(child_id, desc, make_sock);
208 if (pid<0) goto error;
209 *lt_h=lt;
210 return pid;
211 error:
212 if (lt) shm_free(lt);
213 return -1;
214 }
215
216 /**
217 * \brief update internal counters for running new sync sec. timers
218 * @param timers number of basic timer processes
219 * @return 0 on success; -1 on error
220 */
register_sync_timers(int timers)221 int register_sync_timers(int timers)
222 {
223 if(register_procs(timers)<0)
224 return -1;
225 cfg_register_child(timers);
226 return 0;
227 }
228
229 /**
230 * \brief Forks a separate simple sleep() -&- sync periodic timer
231 *
232 * Forks a very basic periodic timer process, that just sleep()s for
233 * the specified interval and then calls the timer function.
234 * The new "sync timer" process execution start immediately, the sleep()
235 * is called first (so the first call to the timer function will happen
236 * \<interval\> seconds after the call to fork_sync_timer)
237 * @param child_id @see fork_process()
238 * @param desc @see fork_process()
239 * @param make_sock @see fork_process()
240 * @param f timer function/callback
241 * @param param parameter passed to the timer function
242 * @param interval interval in seconds.
243 * @return pid of the new process on success, -1 on error
244 * (doesn't return anything in the child process)
245 */
fork_sync_timer(int child_id,char * desc,int make_sock,timer_function * f,void * param,int interval)246 int fork_sync_timer(int child_id, char* desc, int make_sock,
247 timer_function* f, void* param, int interval)
248 {
249 int pid;
250 ticks_t ts1 = 0;
251 ticks_t ts2 = 0;
252
253 pid=fork_process(child_id, desc, make_sock);
254 if (pid<0) return -1;
255 if (pid==0){
256 /* child */
257 interval *= 1000; /* miliseconds */
258 ts2 = interval;
259 if (cfg_child_init()) return -1;
260 for(;;){
261 if (ts2>interval)
262 sleep_us(1000); /* 1 milisecond sleep to catch up */
263 else
264 sleep_us(ts2*1000); /* microseconds sleep */
265 ts1 = get_ticks_raw();
266 cfg_update();
267 f(TICKS_TO_S(ts1), param); /* ticks in sec for compatibility with old
268 * timers */
269 /* adjust the next sleep duration */
270 ts2 = interval - TICKS_TO_MS(get_ticks_raw()) + TICKS_TO_MS(ts1);
271 }
272 }
273 /* parent */
274 return pid;
275 }
276
277
278 /**
279 * \brief Forks a separate simple microsecond-sleep() -&- sync periodic timer
280 *
281 * Forks a very basic periodic timer process, that just us-sleep()s for
282 * the specified interval and then calls the timer function.
283 * The new "sync timer" process execution start immediately, the us-sleep()
284 * is called first (so the first call to the timer function will happen
285 * \<interval\> microseconds after the call to fork_basic_utimer)
286 * @param child_id @see fork_process()
287 * @param desc @see fork_process()
288 * @param make_sock @see fork_process()
289 * @param f timer function/callback
290 * @param param parameter passed to the timer function
291 * @param uinterval interval in micro-seconds.
292 * @return pid of the new process on success, -1 on error
293 * (doesn't return anything in the child process)
294 */
fork_sync_utimer(int child_id,char * desc,int make_sock,utimer_function * f,void * param,int uinterval)295 int fork_sync_utimer(int child_id, char* desc, int make_sock,
296 utimer_function* f, void* param, int uinterval)
297 {
298 int pid;
299 ticks_t ts1 = 0;
300 ticks_t ts2 = 0;
301
302 pid=fork_process(child_id, desc, make_sock);
303 if (pid<0) return -1;
304 if (pid==0){
305 /* child */
306 ts2 = uinterval;
307 if (cfg_child_init()) return -1;
308 for(;;){
309 if(ts2>uinterval)
310 sleep_us(1);
311 else
312 sleep_us(ts2);
313 ts1 = get_ticks_raw();
314 cfg_update();
315 f(TICKS_TO_MS(ts1), param); /* ticks in mili-seconds */
316 ts2 = uinterval - get_ticks_raw() + ts1;
317 }
318 }
319 /* parent */
320 return pid;
321 }
322
323
324 /* number of slots in the wheel timer */
325 #define SR_WTIMER_SIZE 16
326
327 typedef struct sr_wtimer_node {
328 struct sr_wtimer_node *next;
329 uint32_t interval; /* frequency of execution (secs) */
330 uint32_t steps; /* init: interval = loops * SR_WTIMER_SIZE + steps */
331 uint32_t loops;
332 uint32_t eloop;
333 timer_function* f;
334 void* param;
335 } sr_wtimer_node_t;
336
337 typedef struct sr_wtimer {
338 uint32_t itimer;
339 sr_wtimer_node_t *wlist[SR_WTIMER_SIZE];
340 } sr_wtimer_t;
341
342 static sr_wtimer_t *_sr_wtimer = NULL;;
343
344 /**
345 *
346 */
sr_wtimer_init(void)347 int sr_wtimer_init(void)
348 {
349 if(_sr_wtimer!=NULL)
350 return 0;
351 _sr_wtimer = (sr_wtimer_t *)pkg_malloc(sizeof(sr_wtimer_t));
352 if(_sr_wtimer==NULL) {
353 PKG_MEM_ERROR;
354 return -1;
355 }
356
357 memset(_sr_wtimer, 0, sizeof(sr_wtimer_t));
358 register_sync_timers(1);
359 return 0;
360 }
361
362 /**
363 *
364 */
sr_wtimer_add(timer_function * f,void * param,int interval)365 int sr_wtimer_add(timer_function* f, void* param, int interval)
366 {
367 sr_wtimer_node_t *wt;
368 if(_sr_wtimer==NULL) {
369 LM_ERR("wtimer not initialized\n");
370 return -1;
371 }
372
373 wt = (sr_wtimer_node_t*)pkg_malloc(sizeof(sr_wtimer_node_t));
374 if(wt==NULL) {
375 PKG_MEM_ERROR;
376 return -1;
377 }
378 memset(wt, 0, sizeof(sr_wtimer_node_t));
379 wt->f = f;
380 wt->param = param;
381 wt->interval = interval;
382 wt->steps = interval % SR_WTIMER_SIZE;
383 wt->loops = interval / SR_WTIMER_SIZE;
384 wt->eloop = wt->loops;
385 wt->next = _sr_wtimer->wlist[wt->steps];
386 _sr_wtimer->wlist[wt->steps] = wt;
387
388 return 0;
389 }
390
391 /**
392 *
393 */
sr_wtimer_reinsert(uint32_t cs,sr_wtimer_node_t * wt)394 int sr_wtimer_reinsert(uint32_t cs, sr_wtimer_node_t *wt)
395 {
396 uint32_t ts;
397
398 ts = (cs + wt->interval) % SR_WTIMER_SIZE;
399 wt->eloop = wt->interval / SR_WTIMER_SIZE;
400 wt->next = _sr_wtimer->wlist[ts];
401 _sr_wtimer->wlist[ts] = wt;
402
403 return 0;
404 }
405
406 /**
407 *
408 */
sr_wtimer_exec(unsigned int ticks,void * param)409 void sr_wtimer_exec(unsigned int ticks, void *param)
410 {
411 sr_wtimer_node_t *wt;
412 sr_wtimer_node_t *wn;
413 sr_wtimer_node_t *wp;
414 uint32_t cs;
415
416 if(_sr_wtimer==NULL) {
417 LM_ERR("wtimer not initialized\n");
418 return;
419 }
420
421 _sr_wtimer->itimer++;
422 cs = _sr_wtimer->itimer % SR_WTIMER_SIZE;
423 /* uint32_t cl;
424 cl = _sr_wtimer->itimer / SR_WTIMER_SIZE;
425 LM_DBG("wtimer - loop: %u - slot: %u\n", cl, cs); */
426
427 wp = NULL;
428 wt=_sr_wtimer->wlist[cs];
429 while(wt) {
430 wn = wt->next;
431 if(wt->eloop==0) {
432 /* execute timer callback function */
433 wt->f(ticks, wt->param);
434 /* extract and reinsert timer item */
435 if(wp==NULL) {
436 _sr_wtimer->wlist[cs] = wn;
437 } else {
438 wp->next = wn;
439 }
440 sr_wtimer_reinsert(cs, wt);
441 } else {
442 wt->eloop--;
443 wp = wt;
444 }
445 wt = wn;
446 }
447 }
448
449 /**
450 *
451 */
sr_wtimer_start(void)452 int sr_wtimer_start(void)
453 {
454 if(_sr_wtimer==NULL) {
455 LM_ERR("wtimer not initialized\n");
456 return -1;
457 }
458
459 if(fork_sync_timer(-1 /*PROC_TIMER*/, "secondary timer", 1,
460 sr_wtimer_exec, NULL, 1)<0) {
461 LM_ERR("wtimer starting failed\n");
462 return -1;
463 }
464
465 return 0;
466 }
467
468 /* vi: set ts=4 sw=4 tw=79:ai:cindent: */
469