]> git.vomp.tv Git - vompclient-marten.git/blob - timers.cc
EPG tweaks, EPG made NTSC compatible
[vompclient-marten.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   logger->log("Timers", Log::DEBUG, "Timers init start");\r
49 \r
50   threadLock(); // lock here, the thread loop will unlock and wait\r
51   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");\r
52   if (!threadStart())\r
53   {\r
54     shutdown();\r
55     return 0;\r
56   }\r
57 \r
58   logger->log("Timers", Log::DEBUG, "Timers init end");\r
59 \r
60   return 1;\r
61 }\r
62 \r
63 int Timers::shutdown()\r
64 {\r
65   if (!initted) return 0;\r
66   initted = false;\r
67 \r
68   logger->log("Timers", Log::DEBUG, "Timers shutdown start");\r
69 \r
70   threadStop();\r
71 \r
72   TimerList::iterator i;\r
73   UINT numTimers = timerList.size();\r
74   while(numTimers)\r
75   {\r
76     i = timerList.begin();\r
77     delete *i;\r
78     timerList.pop_front();\r
79     --numTimers;\r
80   }\r
81 \r
82   logger->log("Timers", Log::DEBUG, "Timers shutdown end");\r
83 \r
84   return 1;\r
85 }\r
86 \r
87 int Timers::setTimer(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)\r
88 {\r
89   if (!initted) return 0;\r
90 \r
91   logger->log("Timers", Log::DEBUG, "Starting set timer 1");\r
92 \r
93   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");\r
94   threadLock();\r
95 \r
96   // Check that this timer is not already in the list\r
97   TimerList::iterator i;\r
98   Timer* currentTimer = NULL;\r
99   for(i = timerList.begin(); i != timerList.end(); i++)\r
100   {\r
101     currentTimer = *i;\r
102     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))\r
103     {\r
104       // Overwrite an existing timer\r
105       currentTimer->requestedTime.tv_sec = requestedTime;\r
106       currentTimer->requestedTime.tv_nsec = requestedTimeNSEC;\r
107       resetThreadFlag = true;\r
108       threadSignalNoLock();\r
109 \r
110       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2 (b)");\r
111       threadUnlock();\r
112       return 0;\r
113     }\r
114   }\r
115 \r
116   Timer* t = new Timer();\r
117   t->client = client;\r
118   t->clientReference = clientReference;\r
119   t->requestedTime.tv_sec = requestedTime;\r
120   t->requestedTime.tv_nsec = requestedTimeNSEC;\r
121 \r
122   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");\r
123   timerList.push_back(t);\r
124   resetThreadFlag = true;\r
125   threadSignalNoLock();\r
126   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");\r
127   threadUnlock();\r
128 \r
129   logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);\r
130 \r
131   return 1;\r
132 }\r
133 \r
134 int Timers::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)\r
135 {\r
136   struct timespec currentTime;\r
137   clock_gettime(CLOCK_REALTIME, &currentTime);\r
138 \r
139   long int requestedTime;\r
140   long int requestedTimeNSEC;\r
141 \r
142   requestedTime = currentTime.tv_sec + duration.tv_sec;\r
143   requestedTimeNSEC = currentTime.tv_nsec + duration.tv_nsec;\r
144   if (requestedTimeNSEC > 999999999)\r
145   {\r
146     ++requestedTime;\r
147     requestedTimeNSEC -= 1000000000;\r
148     logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");\r
149   }\r
150 \r
151   return setTimer(client, clientReference, requestedTime, requestedTimeNSEC);\r
152 }\r
153 \r
154 int Timers::cancelTimer(TimerReceiver* client, int clientReference)\r
155 {\r
156   if (!initted) return 0;\r
157 \r
158   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());\r
159 \r
160   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");\r
161   threadLock();\r
162   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");\r
163   TimerList::iterator i;\r
164   Timer* currentTimer = NULL;\r
165   for(i = timerList.begin(); i != timerList.end(); i++)\r
166   {\r
167     currentTimer = *i;\r
168     //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);\r
169     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))\r
170     {\r
171       timerList.erase(i);\r
172       logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);\r
173       break;\r
174       // At this point currentTimer is not in the list but might still be nextTimer in the thread\r
175     }\r
176   }\r
177   if (i == timerList.end())\r
178   {\r
179     // no timer found\r
180     logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);\r
181     //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
182     threadUnlock();\r
183     return 0;\r
184   }\r
185 \r
186   resetThreadFlag = true;\r
187   threadSignalNoLock();\r
188   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
189   threadUnlock();\r
190 \r
191 \r
192   return 1;\r
193 }\r
194 \r
195 void Timers::threadMethod()\r
196 {\r
197   struct timespec nextTime;\r
198   Timer* nextTimer = NULL;\r
199   resetThreadFlag = true;\r
200 \r
201   // locked here\r
202 \r
203   while(1)\r
204   {\r
205     if (resetThreadFlag)\r
206     {\r
207       resetThreadFlag = false;\r
208 \r
209       // Work out the next Timer\r
210 \r
211       nextTime.tv_sec = 0;\r
212       nextTime.tv_nsec = 0;\r
213       nextTimer = NULL;\r
214 \r
215       TimerList::iterator i;\r
216       Timer* currentTimer = NULL;\r
217       for(i = timerList.begin(); i != timerList.end(); i++)\r
218       {\r
219         currentTimer = *i;\r
220         if (!nextTimer)\r
221         {\r
222           nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
223           nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
224           nextTimer = currentTimer;\r
225         }\r
226         else\r
227         {\r
228           if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)\r
229           {\r
230             nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
231             nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
232             nextTimer = currentTimer;\r
233           }\r
234           else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)\r
235           {\r
236             if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)\r
237             {\r
238               nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
239               nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
240               nextTimer = currentTimer;\r
241             }\r
242           }\r
243         }\r
244       }\r
245     }\r
246 \r
247     if (nextTimer)\r
248     {\r
249       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
250 \r
251 \r
252       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");\r
253       threadWaitForSignalTimed(&nextTime); // FIXME does this work if the time is in the past?\r
254       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");\r
255 \r
256       // unlocks in the wait\r
257     }\r
258     else\r
259     {\r
260       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");\r
261       threadWaitForSignal();\r
262       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");\r
263       // unlocks in the wait\r
264     }\r
265 \r
266     // ok. we have been signalled or the time has run out\r
267     // This only gets signalled if it is to reset or die\r
268 \r
269     // First check for die..\r
270     threadCheckExit(); // exiting thread with mutex locked\r
271 \r
272     // Check for reset..\r
273     // This can be caused by an addition or deletion to the list\r
274     if (resetThreadFlag) continue;\r
275 \r
276     // timer ran out\r
277 \r
278     Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);\r
279 \r
280     // send this timer to the timer receiver, via the command message queue\r
281     // so that the gui mutex is locked when it happens\r
282 \r
283     Message* m = new Message();\r
284     m->from = this;\r
285     m->to = nextTimer->client;\r
286     m->message = Message::TIMER;\r
287     m->parameter = nextTimer->clientReference;\r
288 \r
289     if (!Command::getInstance()->postMessageIfNotBusy(m))\r
290     {\r
291       // GUI mutex was locked\r
292       // abort this timer delivery - it might be trying to be deleted!\r
293       delete m;\r
294 \r
295       // now unlock the timers mutex for a fraction of a second\r
296       // in case the gui thread is waiting on the timers mutex\r
297       threadUnlock();\r
298       //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");\r
299       //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");\r
300       usleep(10000); // 10ms - too long?\r
301       //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");\r
302       threadLock();\r
303       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");\r
304       resetThreadFlag = true;\r
305     }\r
306     else\r
307     {\r
308       // timer was delivered\r
309       timerList.remove(nextTimer);\r
310       delete nextTimer;\r
311       nextTimer = NULL;\r
312       resetThreadFlag = true;\r
313     }\r
314   }\r
315 }\r
316 \r
317 /*\r
318 \r
319 Avoiding deadlock using the timer class...\r
320 \r
321 Situation:\r
322 \r
323 timer condwait finishes\r
324 timers is about to fire a timer\r
325 timers locks timers-mutex\r
326 \r
327     user presses a button\r
328     command locks gui-mutex\r
329 \r
330 timers tries to get gui-mutex\r
331 \r
332     view receives button\r
333     view wants to delete itself\r
334     view tries to deletetimer\r
335     goes into delete timer\r
336     waits on timers mutex\r
337 \r
338 - deadlock\r
339 \r
340 \r
341 Solution:\r
342 \r
343 timers tries to get gui mutex\r
344 if mutex is locked already abort\r
345 unlock timers mutex\r
346 wait a fraction of time\r
347 (allow other thread to lock timers mutex)\r
348 lock timers mutex\r
349 set reset flag to recalculate\r
350 - if timer has been cancelled next timer will be calced\r
351 - if timer has not been cancelled it will be called next\r
352 \r
353 */\r