]> git.vomp.tv Git - vompclient.git/blob - timers.cc
*** empty log message ***
[vompclient.git] / timers.cc
1 /*
2     Copyright 2004-2007 Chris Tallon
3
4     This file is part of VOMP.
5
6     VOMP 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.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "timers.h"
22
23 #include "log.h"
24 #include "command.h"
25 #include "timerreceiver.h"
26
27 Timers* Timers::instance = NULL;
28
29 Timers::Timers()
30 {
31   if (instance) return;
32   instance = this;
33   initted = false;
34 }
35
36 Timers::~Timers()
37 {
38   instance = NULL;
39 }
40
41 Timers* Timers::getInstance()
42 {
43   return instance;
44 }
45
46 int Timers::init()
47 {
48   if (initted) return 0;
49   initted = true;
50   logger = Log::getInstance();
51
52   threadLock(); // lock here, the thread loop will unlock and wait
53   if (!threadStart())
54   {
55     shutdown();
56     return 0;
57   }
58
59   return 1;
60 }
61
62 int Timers::shutdown()
63 {
64   if (!initted) return 0;
65   initted = false;
66
67   logger->log("Timers", Log::DEBUG, "Timers shutdown start");
68
69   threadStop();
70
71   TimerEvent* timerEvent = NULL;
72   TimerReceiver* client = NULL;
73   int clientReference = 0;
74
75   while(timerList.size())
76   {
77     threadLock();
78     timerEvent = timerList.front();
79     client = timerEvent->client;
80     clientReference = timerEvent->clientReference;
81     threadUnlock();
82
83     cancelTimer(client, clientReference);
84   }
85
86   logger->log("Timers", Log::DEBUG, "Timers shutdown end");
87
88   return 1;
89 }
90
91 bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
92 {
93   if (!initted) return 0;
94
95   logger->log("Timers", Log::DEBUG, "Starting set timer 1");
96
97   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
98   threadLock();
99
100   // Check that this timer is not already in the list
101   TimerList::iterator i;
102   TimerEvent* currentTimerEvent = NULL;
103   for(i = timerList.begin(); i != timerList.end(); i++)
104   {
105     currentTimerEvent = *i;
106
107     if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
108     {
109       // Timer exists already, either waiting or running
110       // Update the clocks
111       currentTimerEvent->requestedTime.tv_sec = requestedTime;
112       currentTimerEvent->requestedTime.tv_nsec = requestedTimeNSEC;
113
114       if (currentTimerEvent->running)
115       {
116         // If this timerEvent is currently running, update the clocks and set the restart flag.
117         // Instead of being deleted in timerEventFinished it will be restarted
118         currentTimerEvent->restartAfterFinish = true;
119         // Don't need to resetThreadFlag because this timer isn't re-live yet
120         threadUnlock();
121         return true;
122       }
123       else
124       {
125         // A waiting timer has been edited
126         resetThreadFlag = true;
127         threadSignalNoLock();
128         threadUnlock();
129         return true;
130       }
131     }
132   }
133
134   // Timer did not exist already
135
136   TimerEvent* t = new TimerEvent();
137   t->client = client;
138   t->clientReference = clientReference;
139   t->requestedTime.tv_sec = requestedTime;
140   t->requestedTime.tv_nsec = requestedTimeNSEC;
141
142   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
143   timerList.push_back(t);
144   resetThreadFlag = true;
145   threadSignalNoLock();
146   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
147   threadUnlock();
148
149   logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
150
151   return true;
152 }
153
154 bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
155 {
156   struct timespec currentTime;
157
158 #ifndef WIN32
159   clock_gettime(CLOCK_REALTIME, &currentTime);
160 #else
161   SYSTEMTIME systime;
162   __int64  filetime;
163   __int64  test;
164   GetSystemTime(&systime);
165   SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
166    currentTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
167    //#error "Hier gibt was zu tun!"
168    currentTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
169 #endif
170
171   long int requestedTime;
172   long int requestedTimeNSEC;
173
174   requestedTime = currentTime.tv_sec + requestedSecs;
175   requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
176   if (requestedTimeNSEC > 999999999)
177   {
178     ++requestedTime;
179     requestedTimeNSEC -= 1000000000;
180     logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
181   }
182
183   return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
184 }
185
186 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
187 {
188   /* This method locks the timers mutex
189      Then one of three things can happen:
190      1. The TimerEvent is found, running = false. This means it hasn't started yet.
191         Delete the timer normally, set resetFlag
192      2. The TimerEvent is found, running = true. This means the timer is currently firing,
193         timercall on the client is being called.
194         a. Thread calling cancelTimer is an external thread: In this case, this thread
195            calling cancelTimer needs to unlock and wait for the timercall thread to get
196            back. (sleeps or signalling)
197         b. the timercall thread is calling cancelTimer. remove any restartAfterFinished
198            request, but otherwise ignore the request to cancelTimer because it has already
199            fired. The timercall thread will return to the calling code and eventually
200            terminate in threadEventFinished.
201      3. The TimerEvent is not found. Client error or the thread returned to
202         the Timers module in between client calling cancelTimer and cancelTimer actually
203         running. Do nothing, return normally.
204
205   By making sure there is no waiting timerevent, and no running timerevent, this ensures
206   that the program cannot segfault because a timer fired on a just deleted object.
207
208   */
209
210   if (!initted) return false;
211
212   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
213
214   while(1)
215   {
216     threadLock();
217
218     TimerList::iterator i;
219     TimerEvent* currentTimerEvent = NULL;
220     for(i = timerList.begin(); i != timerList.end(); i++)
221     {
222       currentTimerEvent = *i;
223       if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
224       {
225         break;
226       }
227     }
228
229     if (i == timerList.end())
230     {
231       // Case 3, no timer found
232       threadUnlock();
233       return true;
234     }
235     else
236     {
237       // Timer found, Case 1 or 2
238
239       if (currentTimerEvent->running == false)
240       {
241         // Case 1. Just delete the timer and reset the thread.
242         timerList.erase(i);
243         delete currentTimerEvent;
244         logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
245         resetThreadFlag = true;
246         threadSignalNoLock();
247         threadUnlock();
248         return true;
249       }
250       else
251       {
252         if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
253         {
254           // Case 2 b.
255           // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
256           logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
257           currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
258           threadUnlock();
259           return true;
260         }
261
262         // Case 2 a. For now, use polling with a 50ms delay.
263         // Don't delete a running timer.
264         // FIXME upgrade me to signalling
265
266         logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
267
268         threadUnlock();
269         MILLISLEEP(50);
270       }
271     }
272   } // end of the big while loop
273 }
274
275 void Timers::timerEventFinished(TimerEvent* timerEvent)
276 {
277   // This function takes out the already timercall'd TimerEvent from the list
278   // Or resets it if restart flag is true
279
280
281   if (!initted) return;
282   threadLock();
283
284   logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
285
286   for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
287   {
288     if (timerEvent != *i) continue;
289
290     if (timerEvent->restartAfterFinish)
291     {
292       logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
293
294       timerEvent->restartAfterFinish = false;
295       timerEvent->running = false;
296       resetThreadFlag = true;
297       threadSignalNoLock();
298     }
299     else
300     {
301       // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
302       timerList.erase(i);
303       logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
304       delete timerEvent;
305     }
306
307     break;
308   }
309   // FIXME At this point, this should signal all threads waiting on cancelTimer
310   threadUnlock();
311
312   // Kill this thread, as it's the one started for the timer event
313   Thread_TYPE::threadSuicide();
314 }
315
316 void Timers::threadMethod()
317 {
318   struct timespec nextTime;
319   TimerEvent* nextTimer = NULL;
320   resetThreadFlag = true;
321
322   // locked here
323
324   while(1)
325   {
326     if (resetThreadFlag)
327     {
328       resetThreadFlag = false;
329
330       // Work out the next Timer
331
332       nextTime.tv_sec = 0;
333       nextTime.tv_nsec = 0;
334       nextTimer = NULL;
335
336       TimerList::iterator i;
337       TimerEvent* currentTimer = NULL;
338       for(i = timerList.begin(); i != timerList.end(); i++)
339       {
340         currentTimer = *i;
341         if (currentTimer->running) continue; // has already been timercall'd
342
343         if (!nextTimer)
344         {
345           nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
346           nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
347           nextTimer = currentTimer;
348         }
349         else
350         {
351           if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
352           {
353             nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
354             nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
355             nextTimer = currentTimer;
356           }
357           else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
358           {
359             if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
360             {
361               nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
362               nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
363               nextTimer = currentTimer;
364             }
365           }
366         }
367       }
368     }
369
370     if (nextTimer)
371     {
372 //##      logger->log("Timers", Log::DEBUG, "List size: %i. nextTimerClient: %p/%i. nextTime.tv_sec: %li. nextTime.tv_nsec: %li", timerList.size(), nextTimer->client, nextTimer->clientReference, nextTime.tv_sec, nextTime.tv_nsec);
373
374
375       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
376       threadWaitForSignalTimed(&nextTime);
377       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
378
379       // unlocks in the wait
380     }
381     else
382     {
383       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
384       threadWaitForSignal();
385       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
386       // unlocks in the wait
387     }
388
389     // Mutex locked again here by exit of wait or timedwait above
390
391     // ok. we have been signalled or the time has run out
392     // This only gets signalled if it is to reset or die
393
394     // First check for die..
395     threadCheckExit(); // exiting thread with mutex locked
396
397     // Check for reset..
398     // This can be caused by an addition or deletion to the list
399     if (resetThreadFlag || (nextTimer == NULL)) continue;
400
401     // timer ran out
402
403     Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
404
405     nextTimer->run(); // sets timerevent to running and starts it
406     resetThreadFlag = true; // find a new timer to wait on
407   }
408 }
409
410
411
412 // Class TimerEvent
413
414 TimerEvent::TimerEvent()
415 {
416   running = false;
417   restartAfterFinish = false;
418   client = NULL;
419   clientReference = 0;
420   requestedTime.tv_sec = 0;
421   requestedTime.tv_nsec = 0;
422 }
423
424 void TimerEvent::threadMethod()
425 {
426   Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
427   client->timercall(clientReference);
428   Timers::getInstance()->timerEventFinished(this); // does not return
429 }
430
431 void TimerEvent::run()
432 {
433   running = true;
434   threadStart();
435 }