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-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
147 long int requestedTime;
\r
148 long int requestedTimeNSEC;
\r
150 requestedTime = currentTime.tv_sec + requestedSecs;
\r
151 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
\r
152 if (requestedTimeNSEC > 999999999)
\r
155 requestedTimeNSEC -= 1000000000;
\r
156 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
\r
159 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
\r
162 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
\r
164 if (!initted) return 0;
\r
166 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
\r
168 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
\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
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
179 timerList.erase(i);
\r
180 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
\r
182 // At this point currentTimer is not in the list but might still be nextTimer in the thread
\r
185 if (i == timerList.end())
\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
194 resetThreadFlag = true;
\r
195 threadSignalNoLock();
\r
196 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
203 void Timers::threadMethod()
\r
205 struct timespec nextTime;
\r
206 Timer* nextTimer = NULL;
\r
207 resetThreadFlag = true;
\r
213 if (resetThreadFlag)
\r
215 resetThreadFlag = false;
\r
217 // Work out the next Timer
\r
219 nextTime.tv_sec = 0;
\r
220 nextTime.tv_nsec = 0;
\r
223 TimerList::iterator i;
\r
224 Timer* currentTimer = NULL;
\r
225 for(i = timerList.begin(); i != timerList.end(); i++)
\r
230 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
231 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
232 nextTimer = currentTimer;
\r
236 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
\r
238 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
239 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
240 nextTimer = currentTimer;
\r
242 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
\r
244 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
\r
246 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
247 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
248 nextTimer = currentTimer;
\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
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
264 // unlocks in the wait
\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
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
277 // First check for die..
\r
278 threadCheckExit(); // exiting thread with mutex locked
\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
286 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
\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
291 Message* m = new Message(); // Timer call, must be injected into master mutex (this is generated outside the mutex)
\r
293 m->to = nextTimer->client;
\r
294 m->message = Message::TIMER;
\r
295 m->parameter = nextTimer->clientReference;
\r
297 if (!Command::getInstance()->postMessageIfNotBusy(m))
\r
299 // GUI mutex was locked
\r
300 // abort this timer delivery - it might be trying to be deleted!
\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
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
311 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
\r
312 resetThreadFlag = true;
\r
316 // timer was delivered
\r
317 timerList.remove(nextTimer);
\r
320 resetThreadFlag = true;
\r
327 Avoiding deadlock using the timer class...
\r
331 timer condwait finishes
\r
332 timers is about to fire a timer
\r
333 timers locks timers-mutex
\r
335 user presses a button
\r
336 command locks gui-mutex
\r
338 timers tries to get gui-mutex
\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
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
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