1 /*
2  * Copyright (C) 2012 Frafos GmbH
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /** @file AmPeriodicThread.cpp */
28 
29 #include "AmPeriodicThread.h"
30 #include <assert.h>
31 
32 #include "log.h"
33 
infinite_loop(struct timeval * tick,unsigned int max_ticks_behind,void * usr_data)34 void AmPeriodicThread::infinite_loop(struct timeval* tick, unsigned int max_ticks_behind, void* usr_data)
35 {
36   struct timeval now,next_tick,diff;
37   unsigned int ticks_passed;
38   unsigned long ms_diff, ms_tick;
39 
40   assert(tick);
41   ms_tick = tick->tv_sec * 1000 + tick->tv_usec / 1000;
42 
43   gettimeofday(&next_tick,NULL);
44 
45   while (true) {
46 
47     gettimeofday(&now,NULL);
48     timeradd(tick,&next_tick,&next_tick);
49 
50     if(timercmp(&now,&next_tick,<)){
51 
52       struct timespec sdiff,rem;
53       timersub(&next_tick,&now,&diff);
54 
55       // detect backward clockdrift
56       ms_diff = diff.tv_sec * 1000 + diff.tv_usec / 1000;
57       if(ms_diff / ms_tick > 1) {
58 	// at least 2 ticks ahead...
59 	next_tick = now;
60 	sdiff.tv_sec  = tick->tv_sec;
61 	sdiff.tv_nsec = tick->tv_usec * 1000;
62 
63 	WARN("clock drift backwards detected (%lu ticks ahead), "
64 	     "resetting sw clock\n", ms_diff / ms_tick - 1);
65       }
66       else {
67 	// everything ok
68 	sdiff.tv_sec  = diff.tv_sec;
69 	sdiff.tv_nsec = diff.tv_usec * 1000;
70       }
71 
72       if(sdiff.tv_nsec > 2000000) // 2 ms
73 	nanosleep(&sdiff,&rem);
74 
75       ticks_passed = 1;
76     }
77     else {
78       // compute missed ticks
79       unsigned long ms_diff;
80       timersub(&now,&next_tick,&diff);
81       ms_diff = diff.tv_sec * 1000 + diff.tv_usec / 1000;
82       ticks_passed = ms_diff / ms_tick + 1;
83 
84       // missed too many ticks: resync
85       if(ticks_passed > max_ticks_behind) {
86 	// resync to clock if clock is farther than max_behind_tick in the future
87 	WARN("clock drift detected (missed %d ticks), resetting sw clock\n",
88 	     ticks_passed);
89 	next_tick = now;
90       }
91     }
92 
93     // execute looping step code
94     if(!looping_step(usr_data))
95       break;
96   }
97 }
98