Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2014 OpenSIPS Solutions |
3 | | * Copyright (C) 2007 Voice Sistem SRL |
4 | | * Copyright (C) 2001-2003 FhG Fokus |
5 | | * |
6 | | * This file is part of opensips, a free SIP server. |
7 | | * |
8 | | * opensips 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 | | * opensips is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | * |
22 | | * History: |
23 | | * -------- |
24 | | * 2003-03-19 replaced all the mallocs/frees w/ pkg_malloc/pkg_free (andrei) |
25 | | * 2003-03-29 cleaning pkg_mallocs introduced (jiri) |
26 | | * 2007-02-02 timer with resolution of microseconds added (bogdan) |
27 | | * 2014-09-11 timer tasks distributed via reactors (bogdan) |
28 | | * 2014-10-03 drop all timer processes (aside keeper) (bogdan) |
29 | | */ |
30 | | |
31 | | /*! |
32 | | * \file |
33 | | * \brief Timer handling |
34 | | */ |
35 | | |
36 | | /* keep this first as it needs to include some glib h file with |
37 | | * special defines enabled (mainly sys/types.h) */ |
38 | | #include "reactor.h" |
39 | | #include "pt_load.h" |
40 | | #include "locking.h" |
41 | | |
42 | | #include <unistd.h> |
43 | | #include <fcntl.h> |
44 | | #include <sys/select.h> |
45 | | #include <sys/time.h> |
46 | | #include <sys/types.h> |
47 | | #include <unistd.h> |
48 | | |
49 | | #include "action.h" |
50 | | #include "timer.h" |
51 | | #include "dprint.h" |
52 | | #include "error.h" |
53 | | #include "ipc.h" |
54 | | #include "config.h" |
55 | | #include "sr_module.h" |
56 | | #include "daemonize.h" |
57 | | #include "cfg_reload.h" |
58 | | #include "mem/mem.h" |
59 | | #include "mem/shm_mem.h" |
60 | | |
61 | | #include <stdlib.h> |
62 | | |
63 | | /* define internal timer to 10 milliseconds */ |
64 | 0 | #define ITIMER_TICK 10000 |
65 | | |
66 | | /* try to synchronize with system time every 5 second(s) */ |
67 | 0 | #define TIMER_SYNC_TICKS 5000000 |
68 | | |
69 | | /* synchronize if drift is greater than internal timer tick */ |
70 | 0 | #define TIMER_MAX_DRIFT_TICKS ITIMER_TICK |
71 | | |
72 | | /* list with all the registered timers */ |
73 | | static struct os_timer *timer_list = NULL; |
74 | | |
75 | | /* list with all the registered utimers */ |
76 | | static struct os_timer *utimer_list = NULL; |
77 | | |
78 | | static unsigned int *jiffies=0; |
79 | | static utime_t *ujiffies=0; |
80 | | static utime_t *ijiffies=0; |
81 | | /* the value of the last timer drift */ |
82 | | static utime_t *ijiffies_drift=0; |
83 | | /* the time of the last timer drift */ |
84 | | static utime_t *ijiffies_drift_base=0; |
85 | | static unsigned short timer_id=0; |
86 | | static int timer_pipe[2]; |
87 | | static struct scaling_profile *s_profile=NULL; |
88 | | |
89 | | static gen_lock_t *tr_list_lock = NULL; |
90 | | static struct os_timer **tr_timer_list = NULL; |
91 | | static struct os_timer **tr_timer_pending = NULL; |
92 | | |
93 | | int timer_fd_out = -1 ; |
94 | | char *timer_auto_scaling_profile = NULL; |
95 | | int timer_workers_no = 1; |
96 | | |
97 | | |
98 | | |
99 | | /* counts the number of timer processes to start with; this number may |
100 | | * change during runtime due auto-scaling */ |
101 | | int timer_count_processes(unsigned int *extra) |
102 | 0 | { |
103 | 0 | if (extra) *extra = 0; |
104 | |
|
105 | 0 | if (s_profile && extra) { |
106 | | /* how many can be forked over th number of procs to start with ?*/ |
107 | 0 | if (s_profile->max_procs > timer_workers_no) |
108 | 0 | *extra = s_profile->max_procs - timer_workers_no; |
109 | 0 | } |
110 | |
|
111 | 0 | return 2/*keeper & trigger*/ + timer_workers_no /*workers to start with*/; |
112 | 0 | } |
113 | | |
114 | | |
115 | | /* ret 0 on success, <0 on error*/ |
116 | | int init_timer(void) |
117 | 0 | { |
118 | 0 | int optval; |
119 | |
|
120 | 0 | jiffies = shm_malloc(sizeof(unsigned int)); |
121 | 0 | ujiffies = shm_malloc(sizeof(utime_t)); |
122 | 0 | ijiffies = shm_malloc(sizeof(utime_t)); |
123 | 0 | ijiffies_drift = shm_malloc(sizeof(utime_t)); |
124 | 0 | ijiffies_drift_base = shm_malloc(sizeof(utime_t)); |
125 | |
|
126 | 0 | if (jiffies==0 || ujiffies==0 || ijiffies==0 || |
127 | 0 | ijiffies_drift==0 || ijiffies_drift_base==0){ |
128 | 0 | LM_CRIT("could not init jiffies\n"); |
129 | 0 | return E_OUT_OF_MEM; |
130 | 0 | } |
131 | | |
132 | 0 | if (UTIMER_TICK>TIMER_TICK*1000000) { |
133 | 0 | LM_CRIT("UTIMER > TIMER!!\n"); |
134 | 0 | return E_CFG; |
135 | 0 | } |
136 | | |
137 | 0 | if ( ((TIMER_TICK*1000000) % UTIMER_TICK)!=0 ) { |
138 | 0 | LM_CRIT("TIMER must be multiple of UTIMER!!\n"); |
139 | 0 | return E_CFG; |
140 | 0 | } |
141 | | |
142 | 0 | *jiffies=0; |
143 | 0 | *ujiffies=0; |
144 | 0 | *ijiffies=0; |
145 | 0 | *ijiffies_drift=0; |
146 | 0 | *ijiffies_drift_base=0; |
147 | | |
148 | | /* create the pipe for dispatching the timer jobs */ |
149 | 0 | if ( pipe(timer_pipe)!=0 ) { |
150 | 0 | LM_ERR("failed to create time pipe (%s)!\n",strerror(errno)); |
151 | 0 | return E_UNSPEC; |
152 | 0 | } |
153 | | /* make reading fd non-blocking */ |
154 | 0 | optval=fcntl(timer_pipe[0], F_GETFL); |
155 | 0 | if (optval==-1){ |
156 | 0 | LM_ERR("fcntl failed: (%d) %s\n", errno, strerror(errno)); |
157 | 0 | return E_UNSPEC; |
158 | 0 | } |
159 | 0 | if (fcntl(timer_pipe[0],F_SETFL,optval|O_NONBLOCK)==-1){ |
160 | 0 | LM_ERR("set non-blocking failed: (%d) %s\n", |
161 | 0 | errno, strerror(errno)); |
162 | 0 | return E_UNSPEC; |
163 | 0 | } |
164 | | /* make visible the "read" part of the pipe */ |
165 | 0 | timer_fd_out = timer_pipe[0]; |
166 | |
|
167 | 0 | if (timer_auto_scaling_profile) { |
168 | 0 | s_profile = get_scaling_profile(timer_auto_scaling_profile); |
169 | 0 | if ( s_profile==NULL) { |
170 | 0 | LM_ERR("undefined auto-scaling profile <%s> for timers\n", |
171 | 0 | timer_auto_scaling_profile); |
172 | 0 | return E_UNSPEC; |
173 | 0 | } |
174 | 0 | auto_scaling_enabled = 1; |
175 | 0 | } |
176 | | |
177 | | /* lock to protect the list of timer task for timer routes */ |
178 | 0 | tr_list_lock = lock_alloc(); |
179 | 0 | if (tr_list_lock==0) { |
180 | 0 | LM_ERR("failed to alloc lock\n"); |
181 | 0 | return E_UNSPEC; |
182 | 0 | } |
183 | | |
184 | 0 | if (lock_init(tr_list_lock)==0) { |
185 | 0 | LM_ERR("failed to init lock\n"); |
186 | 0 | return E_UNSPEC; |
187 | 0 | } |
188 | | |
189 | 0 | tr_timer_list = (struct os_timer**)shm_malloc(sizeof(struct os_timer*)); |
190 | 0 | if (tr_timer_list==NULL) { |
191 | 0 | LM_ERR("failed to alloc timer holder\n"); |
192 | 0 | return E_UNSPEC; |
193 | 0 | } |
194 | 0 | *tr_timer_list = NULL; |
195 | |
|
196 | 0 | tr_timer_pending = (struct os_timer**)shm_malloc(sizeof(struct os_timer*)); |
197 | 0 | if (tr_timer_pending==NULL) { |
198 | 0 | LM_ERR("failed to alloc timer pending holder\n"); |
199 | 0 | return E_UNSPEC; |
200 | 0 | } |
201 | 0 | *tr_timer_pending = NULL; |
202 | | |
203 | |
|
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | | |
208 | | |
209 | | void destroy_timer(void) |
210 | 0 | { |
211 | 0 | if (jiffies){ |
212 | 0 | shm_free(jiffies); jiffies=0; |
213 | 0 | shm_free(ujiffies); ujiffies=0; |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | |
218 | | |
219 | | static inline struct os_timer* new_os_timer(char *label, unsigned short flags, |
220 | | timer_function f, void* param, unsigned int interval) |
221 | 0 | { |
222 | 0 | struct os_timer* t; |
223 | |
|
224 | 0 | if (label==NULL) |
225 | 0 | label = "n/a"; |
226 | |
|
227 | 0 | t=shm_malloc( sizeof(struct os_timer) + strlen(label)+1 ); |
228 | 0 | if (t==0){ |
229 | 0 | LM_ERR("out of pkg memory\n"); |
230 | 0 | return NULL; |
231 | 0 | } |
232 | 0 | t->id=timer_id++; |
233 | 0 | t->flags = flags; |
234 | 0 | t->label = (char*)(t+1); |
235 | 0 | strcpy( t->label, label); |
236 | 0 | t->u.timer_f=f; |
237 | 0 | t->t_param=param; |
238 | 0 | t->interval=interval; |
239 | 0 | t->expires=*jiffies+interval; |
240 | 0 | t->trigger_time = 0; |
241 | 0 | t->time = 0; |
242 | 0 | return t; |
243 | 0 | } |
244 | | |
245 | | |
246 | | /*register a periodic timer; |
247 | | * ret: <0 on error |
248 | | * Hint: if you need it in a module, register it from mod_init or it |
249 | | * won't work otherwise*/ |
250 | | int register_timer(char *label, timer_function f, void* param, |
251 | | unsigned int interval, unsigned short flags) |
252 | 0 | { |
253 | 0 | struct os_timer* t; |
254 | |
|
255 | 0 | flags = flags & (~TIMER_FLAG_IS_UTIMER); /* just to be sure */ |
256 | 0 | t = new_os_timer( label, flags, f, param, interval); |
257 | 0 | if (t==NULL) |
258 | 0 | return E_OUT_OF_MEM; |
259 | | /* insert it into the timer list*/ |
260 | 0 | t->next = timer_list; |
261 | 0 | timer_list = t; |
262 | 0 | return t->id; |
263 | 0 | } |
264 | | |
265 | | |
266 | | int register_utimer(char *label, utimer_function f, void* param, |
267 | | unsigned int interval, unsigned short flags) |
268 | 0 | { |
269 | 0 | struct os_timer* t; |
270 | |
|
271 | 0 | flags = flags | TIMER_FLAG_IS_UTIMER; /* just to be sure */ |
272 | 0 | t = new_os_timer( label, flags, (timer_function*)f, param, interval); |
273 | 0 | if (t==NULL) |
274 | 0 | return E_OUT_OF_MEM; |
275 | | /* insert it into the utimer list*/ |
276 | 0 | t->next = utimer_list; |
277 | 0 | utimer_list = t; |
278 | 0 | return t->id; |
279 | 0 | } |
280 | | |
281 | | |
282 | | struct timer_route_param { |
283 | | unsigned int idx; |
284 | | unsigned int version; |
285 | | }; |
286 | | |
287 | | void route_timer_f(unsigned int ticks, void* param) |
288 | 0 | { |
289 | 0 | struct timer_route_param *tr=(struct timer_route_param *)param; |
290 | 0 | struct script_route sr; |
291 | 0 | struct sip_msg *req; |
292 | 0 | int old_route_type; |
293 | |
|
294 | 0 | if (tr->version!=sroutes->version) { |
295 | 0 | LM_WARN("timer route triggering received for an old cfg version " |
296 | 0 | "%d<>%d\n",tr->version, sroutes->version); |
297 | 0 | return; |
298 | 0 | } |
299 | | |
300 | 0 | sr.name = sroutes->timer[tr->idx].name; |
301 | 0 | sr.a = sroutes->timer[tr->idx].a; |
302 | |
|
303 | 0 | if(sr.a == NULL) { |
304 | 0 | LM_ERR("NULL actions for timer_route '%s'/%d\n", sr.name, tr->idx); |
305 | 0 | return; |
306 | 0 | } |
307 | | |
308 | 0 | req = get_dummy_sip_msg(); |
309 | 0 | if(req == NULL) { |
310 | 0 | LM_ERR("No more memory\n"); |
311 | 0 | return; |
312 | 0 | } |
313 | | |
314 | 0 | swap_route_type(old_route_type, TIMER_ROUTE); |
315 | 0 | run_top_route(sr, req); |
316 | 0 | set_route_type(old_route_type); |
317 | | |
318 | | /* clean whatever extra structures were added by script functions */ |
319 | 0 | release_dummy_sip_msg(req); |
320 | | |
321 | | /* remove all added AVP - here we use all the time the default AVP list */ |
322 | 0 | reset_avps( ); |
323 | 0 | } |
324 | | |
325 | | |
326 | | /* the function will check the timer routes from the current process, |
327 | | * so be carefull where you are running it from */ |
328 | | int register_route_timers(void) |
329 | 0 | { |
330 | 0 | struct timer_route_param *tr_param; |
331 | 0 | struct os_timer *t, *p; |
332 | 0 | int i; |
333 | |
|
334 | 0 | #define move_to_pending( _t) \ |
335 | 0 | while(_t) { \ |
336 | 0 | p = (_t)->next; \ |
337 | 0 | if ((_t)->trigger_time) { \ |
338 | 0 | (_t)->next = *tr_timer_pending; \ |
339 | 0 | *tr_timer_pending = (_t); \ |
340 | 0 | } else { \ |
341 | 0 | shm_free( (_t)->t_param ); \ |
342 | 0 | shm_free( (_t) ); \ |
343 | 0 | } \ |
344 | 0 | (_t) = p; \ |
345 | 0 | } |
346 | |
|
347 | 0 | lock_get(tr_list_lock); |
348 | | |
349 | | /* handle the pending list, remove whatever already finished, |
350 | | * otherwise put back into pending */ |
351 | 0 | t = *tr_timer_pending; |
352 | 0 | *tr_timer_pending = NULL; |
353 | 0 | move_to_pending( t); |
354 | | |
355 | | /* handle the existing list -> free if done or move to pending if |
356 | | * the job is still under execution (for sure triggering cannot be |
357 | | * done anymore as the have the lock here) */ |
358 | 0 | t = *tr_timer_list; |
359 | 0 | move_to_pending( t); |
360 | 0 | *tr_timer_list = NULL; |
361 | | |
362 | | /* convert timer routes to jobs */ |
363 | 0 | for(i = 0; i<TIMER_RT_NO && sroutes->timer[i].a ; i++) |
364 | 0 | { |
365 | 0 | LM_DBG("registering timer route [%s] at %d secs\n", |
366 | 0 | sroutes->timer[i].name, sroutes->timer[i].interval); |
367 | |
|
368 | 0 | tr_param = (struct timer_route_param*) |
369 | 0 | shm_malloc( sizeof(struct timer_route_param) ); |
370 | 0 | if (tr_param==NULL) { |
371 | 0 | LM_ERR("no more mem, skipping route timer [%s]\n", |
372 | 0 | sroutes->timer[i].name); |
373 | 0 | } else { |
374 | 0 | tr_param->idx = i; |
375 | 0 | tr_param->version = sroutes->version; |
376 | 0 | t = new_os_timer( "timer_route", 0, route_timer_f, (void*)tr_param, |
377 | 0 | sroutes->timer[i].interval); |
378 | 0 | if (t==NULL) { |
379 | 0 | LM_ERR("no more mem, skipping route timer [%s]\n", |
380 | 0 | sroutes->timer[i].name); |
381 | 0 | } else { |
382 | | /* insert it into the list*/ |
383 | 0 | t->next = *tr_timer_list; |
384 | 0 | *tr_timer_list = t; |
385 | 0 | } |
386 | 0 | } |
387 | 0 | } |
388 | |
|
389 | 0 | lock_release(tr_list_lock); |
390 | |
|
391 | 0 | return 1; |
392 | 0 | } |
393 | | |
394 | | |
395 | 0 | unsigned int have_ticks(void) { |
396 | 0 | return jiffies==NULL ? 0 : 1; |
397 | 0 | } |
398 | | |
399 | 0 | unsigned int have_uticks(void) { |
400 | 0 | return ujiffies==NULL ? 0 : 1; |
401 | 0 | } |
402 | | |
403 | | unsigned int get_ticks(void) |
404 | 0 | { |
405 | 0 | return *jiffies; |
406 | 0 | } |
407 | | |
408 | | |
409 | | utime_t get_uticks(void) |
410 | 0 | { |
411 | 0 | return *ujiffies; |
412 | 0 | } |
413 | | |
414 | | |
415 | | |
416 | | static inline void timer_ticker(struct os_timer *tlist) |
417 | 0 | { |
418 | 0 | struct os_timer* t; |
419 | 0 | unsigned int j; |
420 | 0 | ssize_t l; |
421 | | |
422 | | /* we need to store the original time as while executing the |
423 | | the handlers, the time may pass, affecting the way we |
424 | | calculate the new expire (expire will include the time |
425 | | taken to run handlers) -bogdan */ |
426 | 0 | j = *jiffies; |
427 | |
|
428 | 0 | for (t=tlist;t; t=t->next){ |
429 | 0 | if (j < t->expires) |
430 | 0 | continue; |
431 | | |
432 | 0 | if (t->trigger_time) { |
433 | 0 | LM_WARN("timer task <%s> already scheduled %lld ms ago" |
434 | 0 | " (now %lld ms), %s\n", t->label, ((utime_t)*ijiffies/1000) - |
435 | 0 | (utime_t)(t->trigger_time/1000), ((utime_t)*ijiffies/1000), |
436 | 0 | t->flags&TIMER_FLAG_SKIP_ON_DELAY ? "skipping execution" : |
437 | 0 | t->flags&TIMER_FLAG_DELAY_ON_DELAY ? "delaying execution" : |
438 | 0 | "pushing a new one"); |
439 | |
|
440 | 0 | if (t->flags&TIMER_FLAG_SKIP_ON_DELAY) { |
441 | | /* skip this execution of the timer handler */ |
442 | 0 | t->expires = j + t->interval; |
443 | 0 | continue; |
444 | 0 | } else if (t->flags&TIMER_FLAG_DELAY_ON_DELAY) { |
445 | | /* delay and merge the executions of the timer handler |
446 | | until the prev one is done */ |
447 | 0 | continue; |
448 | 0 | } else { |
449 | | /* launch the task now, even if overlapping with the |
450 | | already running one */ |
451 | 0 | } |
452 | 0 | } |
453 | 0 | t->expires = j + t->interval; |
454 | 0 | t->trigger_time = *ijiffies; |
455 | 0 | t->time = j; |
456 | | /* push the jobs for execution */ |
457 | 0 | again: |
458 | 0 | l = write( timer_pipe[1], &t, sizeof(t)); |
459 | 0 | if (l==-1) { |
460 | 0 | if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) |
461 | 0 | goto again; |
462 | 0 | LM_ERR("writing failed:[%d] %s, skipping job <%s> at %d s\n", |
463 | 0 | errno, strerror(errno),t->label, j); |
464 | 0 | } |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | | |
469 | | |
470 | | static inline void utimer_ticker(struct os_timer *utlist) |
471 | 0 | { |
472 | 0 | struct os_timer* t; |
473 | 0 | utime_t uj; |
474 | 0 | ssize_t l; |
475 | | |
476 | | /* see comment on timer_ticket */ |
477 | 0 | uj = *ujiffies; |
478 | |
|
479 | 0 | for ( t=utlist ; t ; t=t->next){ |
480 | 0 | if (uj < t->expires) |
481 | 0 | continue; |
482 | | |
483 | 0 | if (t->trigger_time) { |
484 | 0 | LM_WARN("utimer task <%s> already scheduled %lld ms ago" |
485 | 0 | " (now %lld ms), %s\n", t->label, ((utime_t)*ijiffies/1000) - |
486 | 0 | (utime_t)(t->trigger_time/1000), ((utime_t)*ijiffies/1000), |
487 | 0 | t->flags&TIMER_FLAG_SKIP_ON_DELAY ? "skipping execution" : |
488 | 0 | t->flags&TIMER_FLAG_DELAY_ON_DELAY ? "delaying execution" : |
489 | 0 | "pushing a new one"); |
490 | |
|
491 | 0 | if (t->flags&TIMER_FLAG_SKIP_ON_DELAY) { |
492 | | /* skip this execution of the timer handler */ |
493 | 0 | t->expires = uj + t->interval; |
494 | 0 | continue; |
495 | 0 | } else if (t->flags&TIMER_FLAG_DELAY_ON_DELAY) { |
496 | | /* delay the execution of the timer handler |
497 | | until the prev one is done */ |
498 | 0 | continue; |
499 | 0 | } else { |
500 | | /* launch the task now, even if overlapping with the |
501 | | already running one */ |
502 | 0 | } |
503 | 0 | } |
504 | 0 | t->expires = uj + t->interval; |
505 | 0 | t->trigger_time = *ijiffies; |
506 | 0 | t->time = uj; |
507 | | /* push the jobs for execution */ |
508 | 0 | again: |
509 | 0 | l = write( timer_pipe[1], &t, sizeof(t)); |
510 | 0 | if (l==-1) { |
511 | 0 | if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) |
512 | 0 | goto again; |
513 | 0 | LM_ERR("writing failed:[%d] %s, skipping job <%s> at %lld us\n", |
514 | 0 | errno, strerror(errno),t->label, uj); |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | | |
519 | | |
520 | | static void run_timer_process( void ) |
521 | 0 | { |
522 | 0 | unsigned int multiple; |
523 | 0 | unsigned int cnt; |
524 | 0 | struct timeval o_tv; |
525 | 0 | struct timeval tv, comp_tv; |
526 | 0 | utime_t drift; |
527 | 0 | utime_t uinterval; |
528 | 0 | utime_t wait; |
529 | 0 | utime_t ij; |
530 | | |
531 | | /* timer re-calibration to compensate drifting */ |
532 | 0 | #define compute_wait_with_drift(_tv) \ |
533 | 0 | do { \ |
534 | 0 | if ( drift > ITIMER_TICK ) { \ |
535 | 0 | wait = (drift >= uinterval) ? 0 : uinterval-drift; \ |
536 | 0 | _tv.tv_sec = wait / 1000000; \ |
537 | 0 | _tv.tv_usec = wait % 1000000; \ |
538 | 0 | drift -= uinterval-wait; \ |
539 | 0 | } else { \ |
540 | 0 | _tv = o_tv; \ |
541 | 0 | } \ |
542 | 0 | }while(0) |
543 | | |
544 | |
|
545 | 0 | if ( (utimer_list==NULL) || ((TIMER_TICK*1000000) == UTIMER_TICK) ) { |
546 | 0 | o_tv.tv_sec = TIMER_TICK; |
547 | 0 | o_tv.tv_usec = 0; |
548 | 0 | multiple = 1; |
549 | 0 | } else { |
550 | 0 | o_tv.tv_sec = UTIMER_TICK / 1000000; |
551 | 0 | o_tv.tv_usec = UTIMER_TICK % 1000000; |
552 | 0 | multiple = (( TIMER_TICK * 1000000 ) / UTIMER_TICK ) / 1000000; |
553 | 0 | } |
554 | |
|
555 | 0 | LM_DBG(" tv = %ld, %ld, m=%d\n", |
556 | 0 | (long)o_tv.tv_sec,(long)o_tv.tv_usec,multiple); |
557 | |
|
558 | 0 | drift = 0; |
559 | 0 | uinterval = o_tv.tv_sec * 1000000 + o_tv.tv_usec; |
560 | |
|
561 | 0 | if (utimer_list==NULL) { |
562 | | /* only TIMERs, ticking at TIMER_TICK */ |
563 | 0 | for( ; ; ) { |
564 | 0 | ij = *ijiffies; |
565 | 0 | compute_wait_with_drift(comp_tv); |
566 | 0 | tv = comp_tv; |
567 | 0 | select( 0, 0, 0, 0, &tv); |
568 | |
|
569 | 0 | timer_ticker( timer_list); |
570 | 0 | lock_get(tr_list_lock); |
571 | 0 | timer_ticker( *tr_timer_list); |
572 | 0 | lock_release(tr_list_lock); |
573 | |
|
574 | 0 | drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? |
575 | 0 | 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); |
576 | 0 | } |
577 | |
|
578 | 0 | } else |
579 | 0 | if (multiple==1) { |
580 | | /* TIMERs and UTIMERs, ticking together TIMER_TICK (synced) */ |
581 | 0 | for( ; ; ) { |
582 | 0 | ij = *ijiffies; |
583 | 0 | compute_wait_with_drift(comp_tv); |
584 | 0 | tv = comp_tv; |
585 | 0 | select( 0, 0, 0, 0, &tv); |
586 | 0 | timer_ticker( timer_list); |
587 | 0 | lock_get(tr_list_lock); |
588 | 0 | timer_ticker( *tr_timer_list); |
589 | 0 | lock_release(tr_list_lock); |
590 | 0 | utimer_ticker( utimer_list); |
591 | |
|
592 | 0 | drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? |
593 | 0 | 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); |
594 | 0 | } |
595 | |
|
596 | 0 | } else { |
597 | | /* TIMERs and UTIMERs, TIMER_TICK is multiple of UTIMER_TICK */ |
598 | 0 | for( cnt=1 ; ; cnt++ ) { |
599 | 0 | ij = *ijiffies; |
600 | 0 | compute_wait_with_drift(comp_tv); |
601 | 0 | tv = comp_tv; |
602 | 0 | select( 0, 0, 0, 0, &tv); |
603 | 0 | utimer_ticker(utimer_list); |
604 | 0 | if (cnt==multiple) { |
605 | 0 | timer_ticker(timer_list); |
606 | 0 | lock_get(tr_list_lock); |
607 | 0 | timer_ticker( *tr_timer_list); |
608 | 0 | lock_release(tr_list_lock); |
609 | 0 | cnt = 0; |
610 | 0 | } |
611 | |
|
612 | 0 | drift += ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec > (*ijiffies-ij)) ? |
613 | 0 | 0 : *ijiffies-ij - ((utime_t)comp_tv.tv_sec*1000000+comp_tv.tv_usec); |
614 | 0 | } |
615 | 0 | } |
616 | 0 | } |
617 | | |
618 | | static void run_timer_process_jif(void) |
619 | 0 | { |
620 | 0 | unsigned int multiple; |
621 | 0 | unsigned int umultiple; |
622 | 0 | unsigned int cnt; |
623 | 0 | unsigned int ucnt; |
624 | 0 | struct timeval o_tv; |
625 | 0 | struct timeval tv; |
626 | 0 | struct timeval sync_ts, last_ts; |
627 | 0 | stime_t interval, drift; |
628 | 0 | utime_t last_ticks, last_sync = 0; |
629 | |
|
630 | 0 | o_tv.tv_sec = 0; |
631 | 0 | o_tv.tv_usec = ITIMER_TICK; /* internal timer */ |
632 | 0 | multiple = ((TIMER_TICK*1000000)) / (UTIMER_TICK); |
633 | 0 | umultiple = (UTIMER_TICK) / (ITIMER_TICK); |
634 | |
|
635 | 0 | LM_DBG("tv = %ld, %ld, m=%d, mu=%d\n", |
636 | 0 | (long)o_tv.tv_sec,(long)o_tv.tv_usec,multiple,umultiple); |
637 | |
|
638 | 0 | gettimeofday(&last_ts, 0); |
639 | 0 | last_ticks = *ijiffies; |
640 | |
|
641 | 0 | for( cnt=1,ucnt=1 ; ; ucnt++ ) { |
642 | 0 | tv = o_tv; |
643 | 0 | select( 0, 0, 0, 0, &tv); |
644 | | |
645 | | /* update internal timer */ |
646 | 0 | *(ijiffies)+=ITIMER_TICK; |
647 | | |
648 | | /* update public utimer */ |
649 | 0 | if (ucnt==umultiple) { |
650 | 0 | *(ujiffies)+=UTIMER_TICK; |
651 | | /* no overflow test as even if we go for 1 microsecond tick, |
652 | | * this will happen in 14038618 years :P */ |
653 | 0 | ucnt = 0; |
654 | |
|
655 | 0 | cnt++; |
656 | | /* update public timer */ |
657 | 0 | if (cnt==multiple) { |
658 | 0 | *(jiffies)+=TIMER_TICK; |
659 | | /* test for overflow (if tick= 1s =>overflow in 136 years)*/ |
660 | 0 | cnt = 0; |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | /* synchronize with system time if needed */ |
665 | 0 | if (*ijiffies - last_sync >= TIMER_SYNC_TICKS) { |
666 | 0 | last_sync = *ijiffies; |
667 | |
|
668 | 0 | gettimeofday(&sync_ts, 0); |
669 | 0 | interval = (utime_t)sync_ts.tv_sec*1000000 + sync_ts.tv_usec |
670 | 0 | - (utime_t)last_ts.tv_sec*1000000 - last_ts.tv_usec; |
671 | |
|
672 | 0 | drift = interval - (*ijiffies - last_ticks); |
673 | | |
674 | | /* protect against sudden time changes */ |
675 | 0 | if (interval < 0 || drift < 0 || drift > TIMER_SYNC_TICKS) { |
676 | 0 | last_ts = sync_ts; |
677 | 0 | last_ticks = *ijiffies; |
678 | 0 | LM_DBG("System time changed, ignoring...\n"); |
679 | 0 | continue; |
680 | 0 | } |
681 | | |
682 | 0 | if (drift > TIMER_MAX_DRIFT_TICKS) { |
683 | 0 | *(ijiffies_drift_base) = *(ijiffies); |
684 | 0 | *(ijiffies) += (drift / ITIMER_TICK) * ITIMER_TICK; |
685 | 0 | *(ijiffies_drift) = (drift / ITIMER_TICK) * ITIMER_TICK; |
686 | |
|
687 | 0 | ucnt += drift / ITIMER_TICK; |
688 | 0 | *(ujiffies) += (ucnt / umultiple) * (UTIMER_TICK); |
689 | 0 | ucnt = ucnt % umultiple; |
690 | |
|
691 | 0 | cnt += (unsigned int)(drift / (UTIMER_TICK)); |
692 | 0 | *(jiffies) += (cnt / multiple) * TIMER_TICK; |
693 | 0 | cnt = cnt % multiple; |
694 | 0 | } |
695 | 0 | } |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | |
700 | | int start_timer_processes(void) |
701 | 0 | { |
702 | 0 | int id; |
703 | 0 | const struct internal_fork_params |
704 | 0 | ifp_tk = { |
705 | 0 | .proc_desc = "time_keeper", |
706 | 0 | .flags = OSS_PROC_NO_IPC|OSS_PROC_NO_LOAD, |
707 | 0 | .type = TYPE_NONE, |
708 | 0 | }, |
709 | 0 | ifp_timer = { |
710 | 0 | .proc_desc = "timer", |
711 | 0 | .flags = OSS_PROC_NO_IPC|OSS_PROC_NO_LOAD, |
712 | 0 | .type = TYPE_NONE, |
713 | 0 | }; |
714 | | |
715 | | /* |
716 | | * A change of the way timers were run. In the pre-1.5 times, |
717 | | * all timer processes had their own jiffies and just the first |
718 | | * one was doing the global ones. Now, there's a separate process |
719 | | * that increases jiffies - run_timer_process_jif(), and the rest |
720 | | * just use that one. |
721 | | * |
722 | | * The main reason for this change was when a function that relied |
723 | | * on jiffies for its timeouts got called from the timer thread and |
724 | | * was unable to detect timeouts. |
725 | | */ |
726 | 0 | if ( (id=internal_fork(&ifp_tk))<0 ) { |
727 | 0 | LM_CRIT("cannot fork time keeper process\n"); |
728 | 0 | goto error; |
729 | 0 | } else if (id==0) { |
730 | | /* new process */ |
731 | 0 | clean_write_pipeend(); |
732 | |
|
733 | 0 | run_timer_process_jif(); |
734 | 0 | exit(-1); |
735 | 0 | } |
736 | | |
737 | | /* fork a timer-trigger process */ |
738 | 0 | if ( (id=internal_fork(&ifp_timer))<0 ) { |
739 | 0 | LM_CRIT("cannot fork timer process\n"); |
740 | 0 | goto error; |
741 | 0 | } else if (id==0) { |
742 | | /* new process */ |
743 | 0 | clean_write_pipeend(); |
744 | |
|
745 | 0 | run_timer_process( ); |
746 | 0 | exit(-1); |
747 | 0 | } |
748 | | |
749 | 0 | return 0; |
750 | 0 | error: |
751 | 0 | return -1; |
752 | 0 | } |
753 | | |
754 | | |
755 | | inline static int handle_io(struct fd_map* fm, int idx,int event_type) |
756 | 0 | { |
757 | 0 | int n=0; |
758 | |
|
759 | 0 | pt_become_active(); |
760 | |
|
761 | 0 | pre_run_handle_script_reload(fm->app_flags); |
762 | |
|
763 | 0 | switch(fm->type){ |
764 | 0 | case F_TIMER_JOB: |
765 | 0 | handle_timer_job(); |
766 | 0 | break; |
767 | 0 | case F_SCRIPT_ASYNC: |
768 | 0 | async_script_resume_f( fm->fd, fm->data, |
769 | 0 | (event_type==IO_WATCH_TIMEOUT)?1:0 ); |
770 | 0 | break; |
771 | 0 | case F_FD_ASYNC: |
772 | 0 | async_fd_resume( fm->fd, fm->data); |
773 | 0 | break; |
774 | 0 | case F_LAUNCH_ASYNC: |
775 | 0 | async_launch_resume( fm->fd, fm->data); |
776 | 0 | break; |
777 | 0 | case F_IPC: |
778 | 0 | ipc_handle_job(fm->fd); |
779 | 0 | break; |
780 | 0 | default: |
781 | 0 | LM_CRIT("unknown fd type %d in Timer Extra\n", fm->type); |
782 | 0 | n = -1; |
783 | 0 | break; |
784 | 0 | } |
785 | | |
786 | 0 | if (reactor_is_empty() && _termination_in_progress==1) { |
787 | 0 | LM_WARN("reactor got empty while termination in progress\n"); |
788 | 0 | ipc_handle_all_pending_jobs(IPC_FD_READ_SELF); |
789 | 0 | if (reactor_is_empty()) |
790 | 0 | dynamic_process_final_exit(); |
791 | 0 | } |
792 | |
|
793 | 0 | post_run_handle_script_reload(); |
794 | |
|
795 | 0 | pt_become_idle(); |
796 | 0 | return n; |
797 | 0 | } |
798 | | |
799 | | int timer_proc_reactor_init(void) |
800 | 0 | { |
801 | | /* create the reactor for timer proc */ |
802 | 0 | if ( init_worker_reactor( "Timer_extra", RCT_PRIO_MAX)<0 ) { |
803 | 0 | LM_ERR("failed to init reactor\n"); |
804 | 0 | goto error; |
805 | 0 | } |
806 | | |
807 | | /* init: start watching for the IPC jobs */ |
808 | 0 | if (reactor_add_reader(IPC_FD_READ_SELF, F_IPC, RCT_PRIO_ASYNC, NULL)<0){ |
809 | 0 | LM_CRIT("failed to add IPC pipe to reactor\n"); |
810 | 0 | goto error; |
811 | 0 | } |
812 | | |
813 | | /* init: start watching for the timer jobs */ |
814 | 0 | if (reactor_add_reader( timer_fd_out, F_TIMER_JOB, |
815 | 0 | RCT_PRIO_TIMER,NULL)<0){ |
816 | 0 | LM_CRIT("failed to add timer pipe_out to reactor\n"); |
817 | 0 | goto error; |
818 | 0 | } |
819 | 0 | return 0; |
820 | | |
821 | 0 | error: |
822 | 0 | destroy_worker_reactor(); |
823 | 0 | return -1; |
824 | 0 | } |
825 | | |
826 | | |
827 | | static int fork_dynamic_timer_process(void *si_filter) |
828 | 0 | { |
829 | 0 | int p_id; |
830 | 0 | const struct internal_fork_params ifp_th = { |
831 | 0 | .proc_desc = "Timer handler", |
832 | 0 | .flags = OSS_PROC_DYNAMIC|OSS_PROC_NEEDS_SCRIPT, |
833 | 0 | .type = TYPE_TIMER, |
834 | 0 | }; |
835 | |
|
836 | 0 | if ((p_id=internal_fork(&ifp_th))<0){ |
837 | 0 | LM_CRIT("cannot fork Timer handler process\n"); |
838 | 0 | return -1; |
839 | 0 | } else if (p_id==0) { |
840 | | /* new Timer process */ |
841 | | /* set a more detailed description */ |
842 | 0 | set_proc_attrs("Timer handler"); |
843 | 0 | if (timer_proc_reactor_init() < 0 || |
844 | 0 | init_child(20000) < 0) { |
845 | 0 | goto error; |
846 | 0 | } |
847 | | |
848 | 0 | report_conditional_status( 1, 0); /*report success*/ |
849 | | /* the child proc is done read&write) dealing with the status pipe */ |
850 | 0 | clean_read_pipeend(); |
851 | | |
852 | | /* launch the reactor */ |
853 | 0 | reactor_main_loop( 1/*timeout in sec*/, error , ); |
854 | 0 | destroy_worker_reactor(); |
855 | 0 | error: |
856 | 0 | report_failure_status(); |
857 | 0 | LM_ERR("Initializing new process failed, exiting with error \n"); |
858 | 0 | pt[process_no].flags |= OSS_PROC_SELFEXIT; |
859 | 0 | exit( -1); |
860 | 0 | } else { |
861 | | /*parent/main*/ |
862 | 0 | return p_id; |
863 | 0 | } |
864 | 0 | } |
865 | | |
866 | | |
867 | | static void timer_process_graceful_terminate(int sender, void *param) |
868 | 0 | { |
869 | | /* we accept this only from the main proccess */ |
870 | 0 | if (sender!=0) { |
871 | 0 | LM_BUG("graceful terminate received from a non-main process!!\n"); |
872 | 0 | return; |
873 | 0 | } |
874 | 0 | LM_NOTICE("process %d received RPC to terminate from Main\n",process_no); |
875 | | |
876 | | /*remove from reactor all the shared fds, so we stop reading from them */ |
877 | | |
878 | | /*remove timer jobs pipe */ |
879 | 0 | reactor_del_reader( timer_fd_out, -1, 0); |
880 | | |
881 | | /*remove private IPC pipe */ |
882 | 0 | reactor_del_reader( IPC_FD_READ_SELF, -1, 0); |
883 | | |
884 | | /* let's drain the private IPC */ |
885 | 0 | ipc_handle_all_pending_jobs(IPC_FD_READ_SELF); |
886 | | |
887 | | /* what is left now is the reactor are async fd's, so we need to |
888 | | * wait to complete all of them */ |
889 | 0 | if (reactor_is_empty()) |
890 | 0 | dynamic_process_final_exit(); |
891 | | |
892 | | /* the exit will be triggered by the reactor, when empty */ |
893 | 0 | _termination_in_progress = 1; |
894 | 0 | LM_WARN("reactor not empty, waiting for pending async\n"); |
895 | 0 | } |
896 | | |
897 | | |
898 | | int start_timer_extra_processes(int *chd_rank) |
899 | 0 | { |
900 | 0 | int i, p_id; |
901 | 0 | const struct internal_fork_params ifp_th = { |
902 | 0 | .proc_desc = "Timer handler", |
903 | 0 | .flags = OSS_PROC_NEEDS_SCRIPT, |
904 | 0 | .type = TYPE_TIMER, |
905 | 0 | }; |
906 | |
|
907 | 0 | if (auto_scaling_enabled && s_profile && |
908 | 0 | create_process_group( TYPE_TIMER, NULL, s_profile , |
909 | 0 | fork_dynamic_timer_process, timer_process_graceful_terminate)!=0) |
910 | 0 | LM_ERR("failed to create group of TIMER processes, " |
911 | 0 | "auto forking will not be possible\n"); |
912 | |
|
913 | 0 | for( i=0 ; i<timer_workers_no ; i++ ) { |
914 | |
|
915 | 0 | (*chd_rank)++; |
916 | 0 | if ( (p_id=internal_fork(&ifp_th))<0 ) { |
917 | 0 | LM_CRIT("cannot fork Timer handler process\n"); |
918 | 0 | return -1; |
919 | 0 | } else if (p_id==0) { |
920 | | /* new Timer process */ |
921 | | /* set a more detailed description */ |
922 | 0 | set_proc_attrs("Timer handler"); |
923 | 0 | if (timer_proc_reactor_init() < 0 || |
924 | 0 | init_child(*chd_rank) < 0) { |
925 | 0 | report_failure_status(); |
926 | 0 | goto error; |
927 | 0 | } |
928 | | |
929 | 0 | report_conditional_status( (!no_daemon_mode), 0); |
930 | | |
931 | | /* launch the reactor */ |
932 | 0 | reactor_main_loop( 1/*timeout in sec*/, error , ); |
933 | 0 | destroy_worker_reactor(); |
934 | |
|
935 | 0 | exit(-1); |
936 | 0 | } |
937 | | /*parent*/ |
938 | |
|
939 | 0 | } |
940 | | |
941 | 0 | return 0; |
942 | | |
943 | | /* only from child process */ |
944 | 0 | error: |
945 | 0 | exit(-1); |
946 | 0 | } |
947 | | |
948 | | |
949 | | void handle_timer_job(void) |
950 | 0 | { |
951 | 0 | struct os_timer *t; |
952 | 0 | ssize_t l; |
953 | 0 | utime_t _ijiffies,_ijiffies_extra; |
954 | | |
955 | | /* read one "os_timer" pointer from the pipe (non-blocking) */ |
956 | 0 | l = read( timer_fd_out, &t, sizeof(t) ); |
957 | 0 | if (l==-1) { |
958 | 0 | if (errno==EAGAIN || errno==EINTR || errno==EWOULDBLOCK ) |
959 | 0 | return; |
960 | 0 | LM_ERR("read failed:[%d] %s\n", errno, strerror(errno)); |
961 | 0 | return; |
962 | 0 | } |
963 | | |
964 | | |
965 | | /* |
966 | | Scheduling and handling of the timer task happens without drifting |
967 | | ================================================================== |
968 | | [time_keeper proc] *ijiffies increments: |
969 | | V ITIMER_TICK V ITIMER_TICK V |
970 | | ->|<----------------------------->|<------------------------------->|<-- |
971 | | +ITIMER_TICK +ITIMER_TICK +ITIMER_TICK |
972 | | |
973 | | [timer proc] ^schedule timer job |
974 | | t->trigger_time |
975 | | [Timer handler proc] ^handling timer job |
976 | | |
977 | | The timer task was scheduled before a drift adjustement |
978 | | ======================================================= |
979 | | [time_keeper proc] *ijiffies increments: |
980 | | V ITIMER_TICK V ITIMER_TICK V |
981 | | ->|<----------------------------->|<----------->|<----------------->|<-- |
982 | | +ITIMER_TICK +ITIMER_TICK +DRIFT +ITIMER_TICK |
983 | | ^*ijifies_drift_base |
984 | | [timer proc] ^schedule timer job || ^schedule timer job |
985 | | t->trigger_time |
986 | | [Timer handler proc] ^handling timer job |
987 | | */ |
988 | | |
989 | | /* Cache the entry values for jiffies */ |
990 | 0 | _ijiffies = *ijiffies; |
991 | | /* if we read from the queue after or while a drift was detecte |
992 | | * -> take the drift value into consideration too */ |
993 | 0 | _ijiffies_extra = |
994 | 0 | (t->trigger_time > *ijiffies_drift_base) ? 0 : *ijiffies_drift; |
995 | | |
996 | | /* run the handler */ |
997 | 0 | if (t->flags&TIMER_FLAG_IS_UTIMER) { |
998 | |
|
999 | 0 | if (t->trigger_time<(_ijiffies-_ijiffies_extra-ITIMER_TICK) ) { |
1000 | 0 | LM_WARN("utimer job <%s> has a %lld us delay in execution: " |
1001 | 0 | "trigger_time=%lld ijiffies=%lld ijiffies_extra=%lld\n", |
1002 | 0 | t->label, _ijiffies-t->trigger_time-_ijiffies_extra, |
1003 | 0 | t->trigger_time, _ijiffies, _ijiffies_extra); |
1004 | 0 | } |
1005 | |
|
1006 | 0 | t->u.utimer_f( t->time , t->t_param); |
1007 | 0 | t->trigger_time = 0; |
1008 | |
|
1009 | 0 | } else { |
1010 | |
|
1011 | 0 | if (t->trigger_time<(_ijiffies-_ijiffies_extra-ITIMER_TICK) ) { |
1012 | 0 | LM_WARN("timer job <%s> has a %lld us delay in execution: " |
1013 | 0 | "trigger_time=%lld ijiffies=%lld ijiffies_extra=%lld\n", |
1014 | 0 | t->label, _ijiffies-t->trigger_time-_ijiffies_extra, |
1015 | 0 | t->trigger_time, _ijiffies, _ijiffies_extra); |
1016 | 0 | } |
1017 | |
|
1018 | 0 | t->u.timer_f( (unsigned int)t->time , t->t_param); |
1019 | 0 | t->trigger_time = 0; |
1020 | |
|
1021 | 0 | } |
1022 | |
|
1023 | 0 | return; |
1024 | 0 | } |
1025 | | |