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