]> git.vomp.tv Git - vompclient.git/blob - timers.cc
Patch for avoiding incorrect aspect switches
[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::setTimer(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::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)\r
131 {\r
132   struct timespec currentTime;\r
133   clock_gettime(CLOCK_REALTIME, &currentTime);\r
134 \r
135   long int requestedTime;\r
136   long int requestedTimeNSEC;\r
137 \r
138   requestedTime = currentTime.tv_sec + duration.tv_sec;\r
139   requestedTimeNSEC = currentTime.tv_nsec + duration.tv_nsec;\r
140   if (requestedTimeNSEC > 999999999)\r
141   {\r
142     ++requestedTime;\r
143     requestedTimeNSEC -= 1000000000;\r
144     logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");\r
145   }\r
146 \r
147   return setTimer(client, clientReference, requestedTime, requestedTimeNSEC);\r
148 }\r
149 \r
150 int Timers::cancelTimer(TimerReceiver* client, int clientReference)\r
151 {\r
152   if (!initted) return 0;\r
153 \r
154   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());\r
155 \r
156   //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");\r
157   threadLock();\r
158   //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");\r
159   TimerList::iterator i;\r
160   Timer* currentTimer = NULL;\r
161   for(i = timerList.begin(); i != timerList.end(); i++)\r
162   {\r
163     currentTimer = *i;\r
164     //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);\r
165     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))\r
166     {\r
167       timerList.erase(i);\r
168       logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);\r
169       break;\r
170       // At this point currentTimer is not in the list but might still be nextTimer in the thread\r
171     }\r
172   }\r
173   if (i == timerList.end())\r
174   {\r
175     // no timer found\r
176     logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);\r
177     //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
178     threadUnlock();\r
179     return 0;\r
180   }\r
181 \r
182   resetThreadFlag = true;\r
183   threadSignalNoLock();\r
184   //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");\r
185   threadUnlock();\r
186 \r
187 \r
188   return 1;\r
189 }\r
190 \r
191 void Timers::threadMethod()\r
192 {\r
193   struct timespec nextTime;\r
194   Timer* nextTimer = NULL;\r
195   resetThreadFlag = true;\r
196 \r
197   // locked here\r
198 \r
199   while(1)\r
200   {\r
201     if (resetThreadFlag)\r
202     {\r
203       resetThreadFlag = false;\r
204 \r
205       // Work out the next Timer\r
206 \r
207       nextTime.tv_sec = 0;\r
208       nextTime.tv_nsec = 0;\r
209       nextTimer = NULL;\r
210 \r
211       TimerList::iterator i;\r
212       Timer* currentTimer = NULL;\r
213       for(i = timerList.begin(); i != timerList.end(); i++)\r
214       {\r
215         currentTimer = *i;\r
216         if (!nextTimer)\r
217         {\r
218           nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
219           nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
220           nextTimer = currentTimer;\r
221         }\r
222         else\r
223         {\r
224           if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)\r
225           {\r
226             nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
227             nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
228             nextTimer = currentTimer;\r
229           }\r
230           else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)\r
231           {\r
232             if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)\r
233             {\r
234               nextTime.tv_sec = currentTimer->requestedTime.tv_sec;\r
235               nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;\r
236               nextTimer = currentTimer;\r
237             }\r
238           }\r
239         }\r
240       }\r
241     }\r
242 \r
243     if (nextTimer)\r
244     {\r
245 //##      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
246 \r
247 \r
248       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");\r
249       threadWaitForSignalTimed(&nextTime);\r
250       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");\r
251 \r
252       // unlocks in the wait\r
253     }\r
254     else\r
255     {\r
256       //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");\r
257       threadWaitForSignal();\r
258       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");\r
259       // unlocks in the wait\r
260     }\r
261 \r
262     // ok. we have been signalled or the time has run out\r
263     // This only gets signalled if it is to reset or die\r
264 \r
265     // First check for die..\r
266     threadCheckExit(); // exiting thread with mutex locked\r
267 \r
268     // Check for reset..\r
269     // This can be caused by an addition or deletion to the list\r
270     if (resetThreadFlag) continue;\r
271 \r
272     // timer ran out\r
273 \r
274     Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);\r
275 \r
276     // send this timer to the timer receiver, via the command message queue\r
277     // so that the gui mutex is locked when it happens\r
278 \r
279     Message* m = new Message(); // Timer call, must be injected into master mutex (this is generated outside the mutex)\r
280     m->from = this;\r
281     m->to = nextTimer->client;\r
282     m->message = Message::TIMER;\r
283     m->parameter = nextTimer->clientReference;\r
284 \r
285     if (!Command::getInstance()->postMessageIfNotBusy(m))\r
286     {\r
287       // GUI mutex was locked\r
288       // abort this timer delivery - it might be trying to be deleted!\r
289       delete m;\r
290 \r
291       // now unlock the timers mutex for a fraction of a second\r
292       // in case the gui thread is waiting on the timers mutex\r
293       threadUnlock();\r
294       //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");\r
295       //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");\r
296       usleep(10000); // 10ms - too long?\r
297       //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");\r
298       threadLock();\r
299       //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");\r
300       resetThreadFlag = true;\r
301     }\r
302     else\r
303     {\r
304       // timer was delivered\r
305       timerList.remove(nextTimer);\r
306       delete nextTimer;\r
307       nextTimer = NULL;\r
308       resetThreadFlag = true;\r
309     }\r
310   }\r
311 }\r
312 \r
313 /*\r
314 \r
315 Avoiding deadlock using the timer class...\r
316 \r
317 Situation:\r
318 \r
319 timer condwait finishes\r
320 timers is about to fire a timer\r
321 timers locks timers-mutex\r
322 \r
323     user presses a button\r
324     command locks gui-mutex\r
325 \r
326 timers tries to get gui-mutex\r
327 \r
328     view receives button\r
329     view wants to delete itself\r
330     view tries to deletetimer\r
331     goes into delete timer\r
332     waits on timers mutex\r
333 \r
334 - deadlock\r
335 \r
336 \r
337 Solution:\r
338 \r
339 timers tries to get gui mutex\r
340 if mutex is locked already abort\r
341 unlock timers mutex\r
342 wait a fraction of time\r
343 (allow other thread to lock timers mutex)\r
344 lock timers mutex\r
345 set reset flag to recalculate\r
346 - if timer has been cancelled next timer will be calced\r
347 - if timer has not been cancelled it will be called next\r
348 \r
349 */\r