blob: d24c7fce66592d9d16bcbf8c48d9b47fe8fa0781 [file] [log] [blame]
Alexandre Lision51140e12013-12-02 10:54:09 -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 <sys/time.h>
37
38#include <config.h>
39#include <cc++/thread.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 * NOTE: This class is only used internaly.
48 * @author Erik Eliasson
49 * @author Werner Dittmann
50 */
51template <class TOCommand, class TOSubscriber>
52class TPRequest
53{
54
55public:
56
57 TPRequest( TOSubscriber tsi, int timeoutMs, const TOCommand &command):
58 subscriber(tsi)
59 {
60 struct timeval tv;
61 gettimeofday(&tv, NULL );
62
63 when_ms = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000;
64 when_ms += timeoutMs;
65 this->command = command;
66 }
67
68 /**
69 * @param t ms since Epoch
70 */
71 bool happensBefore(uint64 t)
72 {
73 if (when_ms < t) {
74 return true;
75 }
76 if (when_ms > t) {
77 return false;
78 }
79 return false; // if equal it does not "happens_before"
80
81 }
82
83 bool happensBefore(const TPRequest *req){
84 return happensBefore(req->when_ms);
85 }
86
87 /**
88 * Number of milli seconds until timeout from when this method is
89 * called
90 */
91 int getMsToTimeout ()
92 {
93 struct timeval tv;
94 gettimeofday(&tv, NULL );
95
96 uint64 now = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000;
97
98 if (happensBefore(now)) {
99 return 0;
100 }
101 else {
102 return (int)(when_ms - now);
103 }
104 }
105
106 TOCommand getCommand()
107 {
108 return command;
109 }
110
111 TOSubscriber getSubscriber()
112 {
113 return subscriber;
114 }
115
116 /**
117 * Two timeout requests are considered equeal if they have
118 * the same subscriber AND command AND time when they
119 * occur. If one of the time is zero then this is a
120 * wildcard and matches always.
121 */
122 bool operator==(const TPRequest<TOCommand, TOSubscriber> &req)
123 {
124 if (req.subscriber == subscriber &&
125 req.command == command &&
126 req.when_ms == when_ms) {
127 return true;
128 }
129 return false;
130 }
131
132private:
133 TOSubscriber subscriber;
134 uint64 when_ms; // Time since Epoch in ms when the timeout
135 // will happen
136
137 TOCommand command; // Command that will be delivered to the
138 // receiver (subscriber) of the timeout.
139};
140
141/**
142 * Class to generate objects giving timeout functionality.
143 *
144 * @author Erik Eliasson
145 * @author Werner Dittmann
146 */
147template<class TOCommand, class TOSubscriber>
148 class TimeoutProvider : public ost::Thread, ost::Event {
149
150public:
151
152 /**
153 * Timeout Provide Constructor
154 */
155 TimeoutProvider(): requests(), synchLock(), stop(false) { }
156
157 /**
158 * Destructor also terminates the Timeout thread.
159 */
160 ~TimeoutProvider() {
161 terminate();
162 }
163
164 /**
165 * Terminates the Timeout provider thread.
166 */
167 void stopThread(){
168 stop = true;
169 signal(); // signal event to waiting thread
170 }
171
172 /**
173 * Request a timeout trigger.
174 *
175 * @param time_ms Number of milli-seconds until the timeout is
176 * wanted. Note that a small additional period of time is
177 * added that depends on execution speed.
178 * @param subscriber The receiver of the callback when the command has timed
179 * out. This argument must not be NULL.
180 * @param command Specifies the String command to be passed back in the
181 * callback.
182 */
183 void requestTimeout(int32_t time_ms, TOSubscriber subscriber, const TOCommand &command)
184 {
185 TPRequest<TOCommand, TOSubscriber>* request =
186 new TPRequest<TOCommand, TOSubscriber>(subscriber, time_ms, command);
187
188 synchLock.enter();
189
190 if (requests.size()==0) {
191 requests.push_front(request);
192 signal();
193 synchLock.leave();
194 return;
195 }
196 if (request->happensBefore(requests.front())) {
197 requests.push_front(request);
198 signal();
199 synchLock.leave();
200 return;
201 }
202 if (requests.back()->happensBefore(request)){
203 requests.push_back(request);
204 signal();
205 synchLock.leave();
206 return;
207 }
208
209 typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i;
210 for(i = requests.begin(); i != requests.end(); i++ ) {
211 if( request->happensBefore(*i)) {
212 requests.insert(i, request);
213 break;
214 }
215 }
216 signal();
217 synchLock.leave();
218 }
219
220 /**
221 * Removes timeout requests that belong to a subscriber and command.
222 *
223 * @see requestTimeout
224 */
225 void cancelRequest(TOSubscriber subscriber, const TOCommand &command)
226 {
227 synchLock.enter();
228 typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i;
229 for(i = requests.begin(); i != requests.end(); ) {
230 if( (*i)->getCommand() == command &&
231 (*i)->getSubscriber() == subscriber) {
232 i = requests.erase(i);
233 continue;
234 }
235 i++;
236 }
237 synchLock.leave();
238 }
239
240protected:
241
242 void run()
243 {
244 do {
245 synchLock.enter();
246 int32_t time = 3600000;
247 int32_t size = 0;
248 if ((size = requests.size()) > 0) {
249 time = requests.front()->getMsToTimeout();
250 }
251 if (time == 0 && size > 0) {
252 if (stop){ // This must be checked so that we will
253 // stop even if we have timeouts to deliver.
254 synchLock.leave();
255 return;
256 }
257 TPRequest<TOCommand, TOSubscriber>* req = requests.front();
258 TOSubscriber subs = req->getSubscriber();
259 TOCommand command = req->getCommand();
260
261 requests.pop_front();
262
263 synchLock.leave(); // call the command with free Mutex
264 subs->handleTimeout(command);
265 continue;
266 }
267 synchLock.leave();
268 if (stop) { // If we were told to stop while delivering
269 // a timeout we will exit here
270 return;
271 }
272 reset(); // ready to receive triggers again
273 wait(time);
274 if (stop) { // If we are told to exit while waiting we
275 // will exit
276 return;
277 }
278 } while(true);
279 }
280
281private:
282
283 // The timeouts are ordered in the order of which they
284 // will expire. Nearest in future is first in list.
285 std::list<TPRequest<TOCommand, TOSubscriber> *> requests;
286
287 ost::Mutex synchLock; // Protects the internal data structures
288
289 bool stop; // Flag to tell the worker thread
290 // to terminate. Set to true and
291 // wake the worker thread to
292 // terminate it.
293};
294
295#endif
296
297/** EMACS **
298 * Local variables:
299 * mode: c++
300 * c-default-style: ellemtel
301 * c-basic-offset: 4
302 * End:
303 */