1 /*
2 * $Id: wheeltimer.cpp 1224 2009-01-09 09:55:37Z rco $
3 *
4 * Copyright (C) 2007 Raphael Coeffic
5 *
6 * This file is part of SEMS, a free SIP media server.
7 *
8 * SEMS 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. This program is released under
12 * the GPL with the additional exemption that compiling, linking,
13 * and/or using OpenSSL is allowed.
14 *
15 * For a license to use the SEMS software under conditions
16 * other than those described here, or to purchase support for this
17 * software, please contact iptel.org by e-mail at the following addresses:
18 * info@iptel.org
19 *
20 * SEMS is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/time.h>
33
34 #include "AmThread.h"
35 #include "wheeltimer.h"
36
37 #include "log.h"
38
39
~timer()40 timer::~timer()
41 {
42 // DBG("timer::~timer(this=%p)\n",this);
43 }
44
_wheeltimer()45 _wheeltimer::_wheeltimer()
46 : wall_clock(0)
47 {
48 struct timeval now;
49 gettimeofday(&now,NULL);
50 unix_clock.set(now.tv_sec);
51 }
52
~_wheeltimer()53 _wheeltimer::~_wheeltimer()
54 {
55 }
56
insert_timer(timer * t)57 void _wheeltimer::insert_timer(timer* t)
58 {
59 //add new timer to user request list
60 reqs_m.lock();
61 reqs_backlog.push_back(timer_req(t,true));
62 reqs_m.unlock();
63 }
64
remove_timer(timer * t)65 void _wheeltimer::remove_timer(timer* t)
66 {
67 if (t == NULL){
68 return;
69 }
70
71 //add timer to remove to user request list
72 reqs_m.lock();
73 reqs_backlog.push_back(timer_req(t,false));
74 reqs_m.unlock();
75 }
76
77 /** return whether interval has elapsed since old_wall_clock */
interval_elapsed(u_int32_t old_wall_clock,unsigned int interval_ms)78 bool _wheeltimer::interval_elapsed(u_int32_t old_wall_clock, unsigned int interval_ms) {
79 u_int32_t diff = wall_clock - old_wall_clock;
80 return WALL_CLOCK_TO_MS(diff) >= interval_ms;
81 }
82
run()83 void _wheeltimer::run()
84 {
85 struct timeval now,next_tick,diff,tick;
86
87 tick.tv_sec = 0;
88 tick.tv_usec = TIMER_RESOLUTION;
89
90 gettimeofday(&now, NULL);
91 timeradd(&tick,&now,&next_tick);
92
93 while(true){
94
95 gettimeofday(&now,NULL);
96
97 if(timercmp(&now,&next_tick,<)){
98
99 struct timespec sdiff,rem;
100 timersub(&next_tick, &now,&diff);
101
102 sdiff.tv_sec = diff.tv_sec;
103 sdiff.tv_nsec = diff.tv_usec * 1000;
104
105 if(sdiff.tv_nsec > 2000000) // 2 ms
106 nanosleep(&sdiff,&rem);
107 }
108 //else {
109 //printf("missed one tick\n");
110 //}
111
112 gettimeofday(&now,NULL);
113 unix_clock.set(now.tv_sec);
114
115 turn_wheel();
116 timeradd(&tick,&next_tick,&next_tick);
117 }
118 }
119
120
121
update_wheel(int wheel)122 void _wheeltimer::update_wheel(int wheel)
123 {
124 // do not try do update wheel 0
125 if(!wheel)
126 return;
127
128 for(;wheel;wheel--){
129
130 int pos = (wall_clock >> (wheel*BITS_PER_WHEEL))
131 & ((1<<BITS_PER_WHEEL)-1);
132
133 timer *t = (timer*)wheels[wheel][pos].next;
134 while( t ) {
135
136 timer* t1 = (timer*)t->next;
137 place_timer(t,wheel-1);
138 t = t1;
139 }
140
141 wheels[wheel][pos].next = NULL;
142 }
143 }
144
turn_wheel()145 void _wheeltimer::turn_wheel()
146 {
147 u_int32_t mask = ((1<<BITS_PER_WHEEL)-1); // 0x00 00 00 FF
148 int i=0;
149
150 //determine which wheel should be updated
151 for(;i<WHEELS;i++){
152 if((wall_clock & mask) ^ mask)
153 break;
154 mask <<= BITS_PER_WHEEL;
155 }
156
157 //increment time
158 wall_clock++;
159
160 // Update existing timer entries
161 update_wheel(i);
162
163 // Swap the lists for timer insertion/deletion requests
164 reqs_m.lock();
165 reqs_process.swap(reqs_backlog);
166 reqs_m.unlock();
167
168 while(!reqs_process.empty()) {
169 timer_req rq = reqs_process.front();
170 reqs_process.pop_front();
171
172 if(rq.insert) {
173 place_timer(rq.t);
174 }
175 else {
176 delete_timer(rq.t);
177 }
178 }
179
180 //check for expired timer to process
181 process_current_timers();
182 }
183
process_current_timers()184 void _wheeltimer::process_current_timers()
185 {
186 timer *t = (timer *)wheels[0][wall_clock & 0xFF].next;
187
188 while(t){
189
190 timer* t1 = (timer*)t->next;
191
192 t->next = NULL;
193 t->prev = NULL;
194
195 t->fire();
196
197 t = t1;
198 }
199
200 wheels[0][wall_clock & 0xFF].next = NULL;
201 }
202
less_ts(unsigned int t1,unsigned int t2)203 inline bool less_ts(unsigned int t1, unsigned int t2)
204 {
205 // t1 < t2
206 return (t1 - t2 > (unsigned int)(1<<31));
207 }
208
place_timer(timer * t)209 void _wheeltimer::place_timer(timer* t)
210 {
211 if(less_ts(t->expires,wall_clock)){
212
213 // we put the late ones at the beginning of next wheel turn
214 add_timer_to_wheel(t,0,((1<<BITS_PER_WHEEL)-1) & wall_clock);
215
216 return;
217 }
218
219 place_timer(t,WHEELS-1);
220 }
221
place_timer(timer * t,int wheel)222 void _wheeltimer::place_timer(timer* t, int wheel)
223 {
224 unsigned int pos;
225 unsigned int clock_mask = t->expires ^ wall_clock;
226
227 for(; wheel; wheel--){
228
229 if( (clock_mask >> (wheel*BITS_PER_WHEEL))
230 & ((1<<BITS_PER_WHEEL)-1) ) {
231
232 break;
233 }
234 }
235
236 // we went down to wheel 0
237 pos = (t->expires >> (wheel*BITS_PER_WHEEL)) & ((1<<BITS_PER_WHEEL)-1);
238 add_timer_to_wheel(t,wheel,pos);
239 }
240
add_timer_to_wheel(timer * t,int wheel,unsigned int pos)241 void _wheeltimer::add_timer_to_wheel(timer* t, int wheel, unsigned int pos)
242 {
243 t->next = wheels[wheel][pos].next;
244 wheels[wheel][pos].next = t;
245
246 if(t->next){
247 ((timer*)t->next)->prev = t;
248 }
249
250 t->prev = &(wheels[wheel][pos]);
251 }
252
delete_timer(timer * t)253 void _wheeltimer::delete_timer(timer* t)
254 {
255 if(t->prev)
256 t->prev->next = t->next;
257
258 if(t->next)
259 ((timer*)t->next)->prev = t->prev;
260
261 delete t;
262 }
263
264
265 /** EMACS **
266 * Local variables:
267 * mode: c++
268 * c-basic-offset: 4
269 * End:
270 */
271