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