]> git.vomp.tv Git - vompclient.git/blob - timers.cc
Bug fix 2 for duplicating epg rows
[vompclient.git] / timers.cc
1 /*\r
2     Copyright 2004-2005 Chris Tallon\r
3 \r
4     This file is part of VOMP.\r
5 \r
6     VOMP is free software; you can redistribute it and/or modify\r
7     it under the terms of the GNU General Public License as published by\r
8     the Free Software Foundation; either version 2 of the License, or\r
9     (at your option) any later version.\r
10 \r
11     VOMP is distributed in the hope that it will be useful,\r
12     but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14     GNU General Public License for more details.\r
15 \r
16     You should have received a copy of the GNU General Public License\r
17     along with VOMP; if not, write to the Free Software\r
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 */\r
20 \r
21 #include "timers.h"\r
22 \r
23 Timers* Timers::instance = NULL;\r
24 \r
25 Timers::Timers()\r
26 {\r
27   if (instance) return;\r
28   instance = this;\r
29   initted = false;\r
30 }\r
31 \r
32 Timers::~Timers()\r
33 {\r
34   instance = NULL;\r
35 }\r
36 \r
37 Timers* Timers::getInstance()\r
38 {\r
39   return instance;\r
40 }\r
41 \r
42 int Timers::init()\r
43 {\r
44   if (initted) return 0;\r
45   initted = true;\r
46   logger = Log::getInstance();\r
47 \r
48   threadLock(); // lock here, the thread loop will unlock and wait\r
49   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");\r
50   if (!threadStart())\r
51   {\r
52     shutdown();\r
53     return 0;\r
54   }\r
55 \r
56   return 1;\r
57 }\r
58 \r
59 int Timers::shutdown()\r
60 {\r
61   if (!initted) return 0;\r
62   initted = false;\r
63 \r
64   logger->log("Timers", Log::DEBUG, "Timers shutdown start");\r
65 \r
66   threadStop();\r
67 \r
68   TimerList::iterator i;\r
69   UINT numTimers = timerList.size();\r
70   while(numTimers)\r
71   {\r
72     i = timerList.begin();\r
73     delete *i;\r
74     timerList.pop_front();\r
75     --numTimers;\r
76   }\r
77 \r
78   logger->log("Timers", Log::DEBUG, "Timers shutdown end");\r
79 \r
80   return 1;\r
81 }\r
82 \r
83 int Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)\r
84 {\r
85   if (!initted) return 0;\r
86 \r
87   logger->log("Timers", Log::DEBUG, "Starting set timer 1");\r
88 \r
89   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");\r
90   threadLock();\r
91 \r
92   // Check that this timer is not already in the list\r
93   TimerList::iterator i;\r
94   Timer* currentTimer = NULL;\r
95   for(i = timerList.begin(); i != timerList.end(); i++)\r
96   {\r
97     currentTimer = *i;\r
98     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))\r
99     {\r
100       // Overwrite an existing timer\r
101       currentTimer->requestedTime.tv_sec = requestedTime;\r
102       currentTimer->requestedTime.tv_nsec = requestedTimeNSEC;\r
103       resetThreadFlag = true;\r
104       threadSignalNoLock();\r
105 \r
106       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2 (b)");\r
107       threadUnlock();\r
108       return 0;\r
109     }\r
110   }\r
111 \r
112   Timer* t = new Timer();\r
113   t->client = client;\r
114   t->clientReference = clientReference;\r
115   t->requestedTime.tv_sec = requestedTime;\r
116   t->requestedTime.tv_nsec = requestedTimeNSEC;\r
117 \r
118   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");\r
119   timerList.push_back(t);\r
120   resetThreadFlag = true;\r
121   threadSignalNoLock();\r
122   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");\r
123   threadUnlock();\r
124 \r
125   logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);\r
126 \r
127   return 1;\r
128 }\r
129 \r
130 int Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)\r
131 {\r
132   struct timespec currentTime;\r
133 \r
134 #ifndef WIN32\r
135   clock_gettime(CLOCK_REALTIME, &currentTime);\r
136 #else\r
137   SYSTEMTIME systime;\r
138   __int64  filetime;\r
139   __int64  test;\r
140   GetSystemTime(&systime);\r
141   SystemTimeToFileTime(&systime,(FILETIME*)&filetime);\r
142   currentTime.tv_sec=filetime/(10*1000*1000);\r
143   currentTime.tv_nsec=(filetime%(10*1000*1000))*100;\r
144 #endif\r
145 \r
146   long int requestedTime;\r
147   long int requestedTimeNSEC;\r
148 \r
149   requestedTime = currentTime.tv_sec + requestedSecs;\r
150   requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;\r
151   if (requestedTimeNSEC > 999999999)\r
152   {\r
153     ++requestedTime;\r
154     requestedTimeNSEC -= 1000000000;\r
155     logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");\r
156   }\r
157 \r
158   return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);\r
159 }\r
160 \r
161 int Timers::cancelTimer(TimerReceiver* client, int clientReference)\r
162 {\r
163   if (!initted) return 0;\r
164 \r
165   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());\r
166 \r
167   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");\r
168   threadLock();\r
169   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");\r
170   TimerList::iterator i;\r
171   Timer* currentTimer = NULL;\r
172   for(i = timerList.begin(); i != timerList.end(); i++)\r
173   {\r
174     currentTimer = *i;\r
175     //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);\r
176     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))\r
177     {\r
178       timerList.erase(i);\r
179       logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);\r
180       break;\r
181       // At this point currentTimer is not in the list but might still be nextTimer in the thread\r
182     }\r
183   }\r
184   if (i == timerList.end())\r
185   {\r
186     // no timer found\r
187     logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);\r
188     //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
189     threadUnlock();\r
190     return 0;\r
191   }\r
192 \r
193   resetThreadFlag = true;\r
194   threadSignalNoLock();\r
195   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
196   threadUnlock();\r
197 \r
198 \r
199   return 1;\r
200 }\r
201 \r
202 void Timers::threadMethod()\r
203 {\r
204   struct timespec nextTime;\r
205   Timer* nextTimer = NULL;\r
206   resetThreadFlag = true;\r
207 \r
208   // locked here\r
209 \r
210   while(1)\r
211   {\r
212     if (resetThreadFlag)\r
213     {\r
214       resetThreadFlag = false;\r
215 \r
216       // Work out the next Timer\r
217 \r
218       nextTime.tv_sec = 0;\r
219       nextTime.tv_nsec = 0;\r
220       nextTimer = NULL;\r
221 \r
222       TimerList::iterator i;\r
223       Timer* currentTimer = NULL;\r
224       for(i = timerList.begin(); i != timerList.end(); i++)\r
225       {\r
226         currentTimer = *i;\r
227         if (!nextTimer)\r
228         {\r
229           nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
230           nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
231           nextTimer = currentTimer;\r
232         }\r
233         else\r
234         {\r
235           if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)\r
236           {\r
237             nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
238             nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
239             nextTimer = currentTimer;\r
240           }\r
241           else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)\r
242           {\r
243             if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)\r
244             {\r
245               nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
246               nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
247               nextTimer = currentTimer;\r
248             }\r
249           }\r
250         }\r
251       }\r
252     }\r
253 \r
254     if (nextTimer)\r
255     {\r
256 //##      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);\r
257 \r
258 \r
259       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");\r
260       threadWaitForSignalTimed(&nextTime);\r
261       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");\r
262 \r
263       // unlocks in the wait\r
264     }\r
265     else\r
266     {\r
267       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");\r
268       threadWaitForSignal();\r
269       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");\r
270       // unlocks in the wait\r
271     }\r
272 \r
273     // ok. we have been signalled or the time has run out\r
274     // This only gets signalled if it is to reset or die\r
275 \r
276     // First check for die..\r
277     threadCheckExit(); // exiting thread with mutex locked\r
278 \r
279     // Check for reset..\r
280     // This can be caused by an addition or deletion to the list\r
281     if (resetThreadFlag) continue;\r
282 \r
283     // timer ran out\r
284 \r
285     Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);\r
286 \r
287     // send this timer to the timer receiver, via the command message queue\r
288     // so that the gui mutex is locked when it happens\r
289 \r
290     Message* m = new Message(); // Timer call, must be injected into master mutex (this is generated outside the mutex)\r
291     m->from = this;\r
292     m->to = nextTimer->client;\r
293     m->message = Message::TIMER;\r
294     m->parameter = nextTimer->clientReference;\r
295 \r
296     if (!Command::getInstance()->postMessageIfNotBusy(m))\r
297     {\r
298       // GUI mutex was locked\r
299       // abort this timer delivery - it might be trying to be deleted!\r
300       delete m;\r
301 \r
302       // now unlock the timers mutex for a fraction of a second\r
303       // in case the gui thread is waiting on the timers mutex\r
304       threadUnlock();\r
305       //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");\r
306       //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");\r
307       MILLISLEEP(10); // 10ms - too long?\r
308       //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");\r
309       threadLock();\r
310       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");\r
311       resetThreadFlag = true;\r
312     }\r
313     else\r
314     {\r
315       // timer was delivered\r
316       timerList.remove(nextTimer);\r
317       delete nextTimer;\r
318       nextTimer = NULL;\r
319       resetThreadFlag = true;\r
320     }\r
321   }\r
322 }\r
323 \r
324 /*\r
325 \r
326 Avoiding deadlock using the timer class...\r
327 \r
328 Situation:\r
329 \r
330 timer condwait finishes\r
331 timers is about to fire a timer\r
332 timers locks timers-mutex\r
333 \r
334     user presses a button\r
335     command locks gui-mutex\r
336 \r
337 timers tries to get gui-mutex\r
338 \r
339     view receives button\r
340     view wants to delete itself\r
341     view tries to deletetimer\r
342     goes into delete timer\r
343     waits on timers mutex\r
344 \r
345 - deadlock\r
346 \r
347 \r
348 Solution:\r
349 \r
350 timers tries to get gui mutex\r
351 if mutex is locked already abort\r
352 unlock timers mutex\r
353 wait a fraction of time\r
354 (allow other thread to lock timers mutex)\r
355 lock timers mutex\r
356 set reset flag to recalculate\r
357 - if timer has been cancelled next timer will be calced\r
358 - if timer has not been cancelled it will be called next\r
359 \r
360 */\r