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