blob: d7b9bb5a07b2dc5207dce00187c8ab7be53b9b8a [file] [log] [blame]
Alexandre Lision7fd5d3d2013-12-04 13:06:40 -05001/*
2 * Copyright (C) 2006, 2005, 2004 Erik Eliasson, Johan Bilien, Werner Dittmann
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19
20#ifndef _TIMEOUTPROVIDER_H_
21#define _TIMEOUTPROVIDER_H_
22
23/**
24 * Provides a way to request timeouts after a number of milli seconds.
25 *
26 * A command is associated to each timeout.
27 *
28 * Modified to use the common c++ library functions and the STL
29 * list by Werner Dittmann.
30 *
31 * @author Erik Eliasson, eliasson@it.kth.se, 2003
32 * @author Werner Dittmann <Werner.Dittmann@t-online.de>
33 */
34
35#include <list>
36#include <stdint.h>
37
38#include <common/Thread.h>
39#include <common/osSpecifics.h>
40
41/**
42 * Represents a request of a "timeout" (delivery of a command to a
43 * "timeout receiver" after at least a specified time period).
44 *
45 * Slightly modified to use gettimeofday directly.
46 *
47 * @author Werner Dittmann
48 */
49template <class TOCommand, class TOSubscriber>
50class TPRequest
51{
52
53public:
54
55 TPRequest( TOSubscriber tsi, int timeoutMs, const TOCommand &command):
56 subscriber(tsi)
57 {
58 when_ms = zrtpGetTickCount();
59
60 when_ms += timeoutMs;
61 this->command = command;
62 }
63
64 /**
65 * @param t ms since Epoch
66 */
67 bool happensBefore(uint64_t t)
68 {
69 if (when_ms < t) {
70 return true;
71 }
72 if (when_ms > t) {
73 return false;
74 }
75 return false; // if equal it does not "happens_before"
76
77 }
78
79 bool happensBefore(const TPRequest *req){
80 return happensBefore(req->when_ms);
81 }
82
83 /**
84 * Number of milli seconds until timeout from when this method is
85 * called
86 */
87 int getMsToTimeout ()
88 {
89 uint64_t now = zrtpGetTickCount();
90
91 if (happensBefore(now)) {
92 return 0;
93 }
94 else {
95 return (int)(when_ms - now);
96 }
97 }
98
99 TOCommand getCommand()
100 {
101 return command;
102 }
103
104 TOSubscriber getSubscriber()
105 {
106 return subscriber;
107 }
108
109 /**
110 * Two timeout requests are considered equeal if they have
111 * the same subscriber AND command AND time when they
112 * occur. If one of the time is zero then this is a
113 * wildcard and matches always.
114 */
115 bool operator==(const TPRequest<TOCommand, TOSubscriber> &req)
116 {
117 if (req.subscriber == subscriber &&
118 req.command == command &&
119 req.when_ms == when_ms) {
120 return true;
121 }
122 return false;
123 }
124
125private:
126 TOSubscriber subscriber;
127 uint64_t when_ms; // Time since Epoch in ms when the timeout
128 // will happen
129
130 TOCommand command; // Command that will be delivered to the
131 // receiver (subscriber) of the timeout.
132};
133
134/**
135 * Class to generate objects giving timeout functionality.
136 *
137 * @author Erik Eliasson
138 * @author Werner Dittmann
139 */
140template<class TOCommand, class TOSubscriber>
141class TimeoutProvider : public CThread {
142
143private:
144
145 // The timeouts are ordered in the order of which they
146 // will expire. Nearest in future is first in list.
147 std::list<TPRequest<TOCommand, TOSubscriber> *> requests;
148
149 CMutexClass synchLock;
150 CEventClass timeEvent;
151
152 bool stop; // Flag to tell the worker thread
153 // to terminate. Set to true and
154 // wake the worker thread to
155 // terminate it.
156
157public:
158
159 /**
160 * Timeout Provide Constructor
161 */
162 TimeoutProvider(): requests(), stop(false) { }
163
164 /**
165 * Destructor also terminates the Timeout thread.
166 */
167 ~TimeoutProvider() {
168 stop = true;
169 timeEvent.Set();
170 }
171
172 /**
173 * Terminates the Timeout provider thread.
174 */
175 void stopThread(){
176 stop = true;
177 timeEvent.Set();
178 }
179
180 /**
181 * Request a timeout trigger.
182 *
183 * @param time_ms Number of milli-seconds until the timeout is
184 * wanted. Note that a small additional period of time is
185 * added that depends on execution speed.
186 * @param subscriber The receiver of the callback when the command has timed
187 * out. This argument must not be NULL.
188 * @param command Specifies the String command to be passed back in the
189 * callback.
190 */
191 void requestTimeout(int32_t time_ms, TOSubscriber subscriber, const TOCommand &command)
192 {
193 TPRequest<TOCommand, TOSubscriber>* request =
194 new TPRequest<TOCommand, TOSubscriber>(subscriber, time_ms, command);
195
196 synchLock.Lock();
197
198 if (requests.size()==0) {
199 requests.push_front(request);
200 timeEvent.Set();
201 synchLock.Unlock();
202 return;
203 }
204 if (request->happensBefore(requests.front())) {
205 requests.push_front(request);
206 timeEvent.Set();
207 synchLock.Unlock();
208 return;
209 }
210 if (requests.back()->happensBefore(request)){
211 requests.push_back(request);
212 timeEvent.Set();
213 synchLock.Unlock();
214 return;
215 }
216
217 typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i;
218 for(i = requests.begin(); i != requests.end(); i++ ) {
219 if( request->happensBefore(*i)) {
220 requests.insert(i, request);
221 break;
222 }
223 }
224 timeEvent.Set();
225 synchLock.Unlock();
226 }
227
228 /**
229 * Removes timeout requests that belong to a subscriber and command.
230 *
231 * @see requestTimeout
232 */
233 void cancelRequest(TOSubscriber subscriber, const TOCommand &command)
234 {
235 synchLock.Lock();
236 typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i;
237 for(i = requests.begin(); i != requests.end(); ) {
238 if( (*i)->getCommand() == command &&
239 (*i)->getSubscriber() == subscriber) {
240 i = requests.erase(i);
241 continue;
242 }
243 i++;
244 }
245 synchLock.Unlock();
246 }
247
248 virtual BOOL OnTask(LPVOID lpv)
249 {
250 do {
251 synchLock.Lock();
252 int32_t time = 3600000;
253 int32_t size = 0;
254
255 if ((size = requests.size()) > 0) {
256 time = requests.front()->getMsToTimeout();
257 }
258 if (time == 0 && size > 0) {
259 TPRequest<TOCommand, TOSubscriber>* req = requests.front();
260 TOSubscriber subs = req->getSubscriber();
261 TOCommand command = req->getCommand();
262
263 requests.pop_front();
264 if (stop) { // This must be checked so that we will
265 synchLock.Unlock();
266 return FALSE;
267 }
268 synchLock.Unlock(); // call the command with free Mutex
269
270 subs->handleTimeout(command);
271 continue;
272 }
273 else
274 synchLock.Unlock();
275 if (stop) { // If we are told to exit while executing cmd
276 return FALSE;
277 }
278 timeEvent.Wait(time);
279 timeEvent.Reset();
280 if (stop) { // If we are told to exit while waiting we will exit
281 return FALSE;
282 }
283
284 } while (true);
285 }
286
287};
288
289#endif