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