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