Coverage Report

Created: 2025-07-11 06:28

/src/opensips/timer.c
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