2 Copyright 2004-2005 Chris Tallon
\r
4 This file is part of VOMP.
\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
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
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
23 Timers* Timers::instance = NULL;
\r
27 if (instance) return;
\r
37 Timers* Timers::getInstance()
\r
44 if (initted) return 0;
\r
46 logger = Log::getInstance();
\r
48 threadLock(); // lock here, the thread loop will unlock and wait
\r
49 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");
\r
59 int Timers::shutdown()
\r
61 if (!initted) return 0;
\r
64 logger->log("Timers", Log::DEBUG, "Timers shutdown start");
\r
68 TimerList::iterator i;
\r
69 UINT numTimers = timerList.size();
\r
72 i = timerList.begin();
\r
74 timerList.pop_front();
\r
78 logger->log("Timers", Log::DEBUG, "Timers shutdown end");
\r
83 int Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
\r
85 if (!initted) return 0;
\r
87 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
\r
89 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
\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
98 if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
\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
106 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2 (b)");
\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
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
125 logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
\r
130 int Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
\r
132 struct timespec currentTime;
\r
135 clock_gettime(CLOCK_REALTIME, ¤tTime);
\r
137 SYSTEMTIME systime;
\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
146 long int requestedTime;
\r
147 long int requestedTimeNSEC;
\r
149 requestedTime = currentTime.tv_sec + requestedSecs;
\r
150 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
\r
151 if (requestedTimeNSEC > 999999999)
\r
154 requestedTimeNSEC -= 1000000000;
\r
155 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
\r
158 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
\r
161 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
\r
163 if (!initted) return 0;
\r
165 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
\r
167 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
\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
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
178 timerList.erase(i);
\r
179 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
\r
181 // At this point currentTimer is not in the list but might still be nextTimer in the thread
\r
184 if (i == timerList.end())
\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
193 resetThreadFlag = true;
\r
194 threadSignalNoLock();
\r
195 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
202 void Timers::threadMethod()
\r
204 struct timespec nextTime;
\r
205 Timer* nextTimer = NULL;
\r
206 resetThreadFlag = true;
\r
212 if (resetThreadFlag)
\r
214 resetThreadFlag = false;
\r
216 // Work out the next Timer
\r
218 nextTime.tv_sec = 0;
\r
219 nextTime.tv_nsec = 0;
\r
222 TimerList::iterator i;
\r
223 Timer* currentTimer = NULL;
\r
224 for(i = timerList.begin(); i != timerList.end(); i++)
\r
229 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
230 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
231 nextTimer = currentTimer;
\r
235 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
\r
237 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
238 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
239 nextTimer = currentTimer;
\r
241 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
\r
243 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
\r
245 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
246 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
247 nextTimer = currentTimer;
\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
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
263 // unlocks in the wait
\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
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
276 // First check for die..
\r
277 threadCheckExit(); // exiting thread with mutex locked
\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
285 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
\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
290 Message* m = new Message(); // Timer call, must be injected into master mutex (this is generated outside the mutex)
\r
292 m->to = nextTimer->client;
\r
293 m->message = Message::TIMER;
\r
294 m->parameter = nextTimer->clientReference;
\r
296 if (!Command::getInstance()->postMessageIfNotBusy(m))
\r
298 // GUI mutex was locked
\r
299 // abort this timer delivery - it might be trying to be deleted!
\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
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
310 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
\r
311 resetThreadFlag = true;
\r
315 // timer was delivered
\r
316 timerList.remove(nextTimer);
\r
319 resetThreadFlag = true;
\r
326 Avoiding deadlock using the timer class...
\r
330 timer condwait finishes
\r
331 timers is about to fire a timer
\r
332 timers locks timers-mutex
\r
334 user presses a button
\r
335 command locks gui-mutex
\r
337 timers tries to get gui-mutex
\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
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
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