1 /*
2  * Copyright (C) 2011 Stefan Sayer
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 
28 #include "AmSessionContainer.h"
29 
30 #include "AmAppTimer.h"
31 #include "log.h"
32 
33 using std::map;
34 
35 class app_timer : public timer
36 {
37   string q_id;
38   int    timer_id;
39 
40  public:
app_timer(const string & q_id,int timer_id,unsigned int expires)41   app_timer(const string& q_id, int timer_id, unsigned int expires)
42     : timer(expires), q_id(q_id), timer_id(timer_id) {}
43 
~app_timer()44   ~app_timer() {}
45 
get_id()46   int get_id() { return timer_id; }
get_q_id()47   string get_q_id() { return q_id; }
48 
49   // timer interface
fire()50   void fire() {
51     AmAppTimer::instance()->app_timer_cb(this);
52   }
53 };
54 
55 class direct_app_timer
56   : public timer
57 {
58 public:
59   DirectAppTimer* dt;
60 
direct_app_timer(DirectAppTimer * dt,unsigned int expires)61   direct_app_timer(DirectAppTimer* dt, unsigned int expires)
62     : timer(expires), dt(dt) {}
63 
~direct_app_timer()64   ~direct_app_timer() {}
65 
fire()66   void fire() {
67     AmAppTimer::instance()->direct_app_timer_cb(this);
68   }
69 };
70 
_AmAppTimer()71 _AmAppTimer::_AmAppTimer()
72   : direct_timers_mut(true)
73 {
74 }
75 
~_AmAppTimer()76 _AmAppTimer::~_AmAppTimer() {
77 }
78 
app_timer_cb(app_timer * at)79 void _AmAppTimer::app_timer_cb(app_timer* at)
80 {
81   user_timers_mut.lock();
82   app_timer* at_local = erase_timer(at->get_q_id(), at->get_id());
83 
84   if (NULL != at_local) {
85 
86     if (at_local != at) {
87       DBG("timer was reset while expiring - not firing timer\n");
88       // we'd better re-insert at_local into user_timers
89       // what happens else, when at_local get fired ???
90       user_timers[at->get_q_id()][at->get_id()] = at_local;
91     } else {
92       DBG("timer fired: %d for '%s'\n", at->get_id(), at->get_q_id().c_str());
93       AmSessionContainer::instance()->postEvent(at->get_q_id(),
94 						new AmTimeoutEvent(at->get_id()));
95       delete at;
96     }
97 
98   } else {
99     DBG("timer %d for '%s' already removed\n",
100 	at->get_id(), at->get_q_id().c_str());
101     // will be deleted by wheeltimer
102   }
103 
104   user_timers_mut.unlock();
105 }
106 
direct_app_timer_cb(direct_app_timer * t)107 void _AmAppTimer::direct_app_timer_cb(direct_app_timer* t)
108 {
109   DirectAppTimer* dt = t->dt;
110 
111   direct_timers_mut.lock();
112   DirectTimers::iterator dt_it = direct_timers.find(dt);
113   if(dt_it != direct_timers.end()){
114     if(dt_it->second != t) {
115       // timer has been re-initialized
116       // with the same pointer... do not trigger!
117       // it will be deleted later by the wheeltimer
118     }
119     else {
120       // everything ok:
121 
122       // remove stuff
123       direct_timers.erase(dt_it);
124       delete t;
125 
126       // finally fire this timer!
127       dt->fire();
128     }
129   }
130   direct_timers_mut.unlock();
131 }
132 
erase_timer(const string & q_id,int id)133 app_timer* _AmAppTimer::erase_timer(const string& q_id, int id)
134 {
135   app_timer* res = NULL;
136 
137   map<string, map<int, app_timer*> >::iterator it=user_timers.find(q_id);
138   if (it != user_timers.end()) {
139     map<int, app_timer*>::iterator t_it = it->second.find(id);
140     if (t_it != it->second.end()) {
141       res = t_it->second;
142       it->second.erase(t_it);
143       if (it->second.empty())
144 	user_timers.erase(it);
145     }
146   }
147 
148   return res;
149 }
150 
create_timer(const string & q_id,int id,unsigned int expires)151 app_timer* _AmAppTimer::create_timer(const string& q_id, int id,
152 				     unsigned int expires)
153 {
154   app_timer* timer = new app_timer(q_id, id, expires);
155   if (!timer)
156     return NULL;
157 
158   user_timers[q_id][id] = timer;
159 
160   return timer;
161 }
162 
163 #define MAX_TIMER_SECONDS 365*24*3600 // one year, well below 1<<31
164 
setTimer(const string & eventqueue_name,int timer_id,double timeout)165 void _AmAppTimer::setTimer(const string& eventqueue_name, int timer_id, double timeout) {
166 
167   // microseconds
168   unsigned int expires;
169   if (timeout < 0) { // in the past
170     expires = 0;
171   } else if (timeout > MAX_TIMER_SECONDS) { // more than one year
172     ERROR("Application requesting timer %d for '%s' with timeout %f, "
173 	  "clipped to maximum of one year\n", timer_id, eventqueue_name.c_str(), timeout);
174     expires = (double)MAX_TIMER_SECONDS*1000.0*1000.0 / (double)TIMER_RESOLUTION;
175   } else {
176     expires = timeout*1000.0*1000.0 / (double)TIMER_RESOLUTION;
177   }
178 
179   expires += wall_clock;
180 
181   user_timers_mut.lock();
182   app_timer* t = erase_timer(eventqueue_name, timer_id);
183   if (NULL != t) {
184     remove_timer(t);
185   }
186   t = create_timer(eventqueue_name, timer_id, expires);
187   if (NULL != t) {
188     insert_timer(t);
189   }
190   user_timers_mut.unlock();
191 }
192 
removeTimer(const string & eventqueue_name,int timer_id)193 void _AmAppTimer::removeTimer(const string& eventqueue_name, int timer_id)
194 {
195   user_timers_mut.lock();
196   app_timer* t = erase_timer(eventqueue_name, timer_id);
197   if (NULL != t) {
198     remove_timer(t);
199   }
200   user_timers_mut.unlock();
201 }
202 
removeTimers(const string & eventqueue_name)203 void _AmAppTimer::removeTimers(const string& eventqueue_name)
204 {
205   user_timers_mut.lock();
206   TimerQueues::iterator it=user_timers.find(eventqueue_name);
207   if (it != user_timers.end()) {
208     for (AppTimers::iterator t_it =
209 	   it->second.begin(); t_it != it->second.end(); t_it++) {
210       if (NULL != t_it->second)
211 	remove_timer(t_it->second);
212     }
213     user_timers.erase(it);
214   }
215   user_timers_mut.unlock();
216 }
217 
setTimer_unsafe(DirectAppTimer * t,double timeout)218 void _AmAppTimer::setTimer_unsafe(DirectAppTimer* t, double timeout)
219 {
220   unsigned int expires = timeout*1000.0*1000.0 / (double)TIMER_RESOLUTION;
221   expires += wall_clock;
222 
223   direct_app_timer* dt = new direct_app_timer(t,expires);
224   if(!dt) return;
225 
226   DirectTimers::iterator dt_it = direct_timers.find(t);
227   if(dt_it != direct_timers.end()){
228     remove_timer(dt_it->second);
229     dt_it->second = dt;
230   }
231   else {
232     direct_timers[t] = dt;
233   }
234   insert_timer(dt);
235 }
236 
setTimer(DirectAppTimer * t,double timeout)237 void _AmAppTimer::setTimer(DirectAppTimer* t, double timeout)
238 {
239   direct_timers_mut.lock();
240   setTimer_unsafe(t,timeout);
241   direct_timers_mut.unlock();
242 }
243 
removeTimer_unsafe(DirectAppTimer * t)244 void _AmAppTimer::removeTimer_unsafe(DirectAppTimer* t)
245 {
246   DirectTimers::iterator dt_it = direct_timers.find(t);
247   if(dt_it != direct_timers.end()){
248     remove_timer(dt_it->second);
249     direct_timers.erase(dt_it);
250   }
251 }
252 
removeTimer(DirectAppTimer * t)253 void _AmAppTimer::removeTimer(DirectAppTimer* t)
254 {
255   direct_timers_mut.lock();
256   removeTimer_unsafe(t);
257   direct_timers_mut.unlock();
258 }
259