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 logger->log("Timers", Log::DEBUG, "Timers init start");
\r
50 threadLock(); // lock here, the thread loop will unlock and wait
\r
51 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");
\r
58 logger->log("Timers", Log::DEBUG, "Timers init end");
\r
63 int Timers::shutdown()
\r
65 if (!initted) return 0;
\r
68 logger->log("Timers", Log::DEBUG, "Timers shutdown start");
\r
72 TimerList::iterator i;
\r
73 UINT numTimers = timerList.size();
\r
76 i = timerList.begin();
\r
78 timerList.pop_front();
\r
82 logger->log("Timers", Log::DEBUG, "Timers shutdown end");
\r
87 int Timers::setTimer(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
\r
89 if (!initted) return 0;
\r
91 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
\r
93 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
\r
96 // Check that this timer is not already in the list
\r
97 TimerList::iterator i;
\r
98 Timer* currentTimer = NULL;
\r
99 for(i = timerList.begin(); i != timerList.end(); i++)
\r
102 if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
\r
104 // Overwrite an existing timer
\r
105 currentTimer->requestedTime.tv_sec = requestedTime;
\r
106 currentTimer->requestedTime.tv_nsec = requestedTimeNSEC;
\r
107 resetThreadFlag = true;
\r
108 threadSignalNoLock();
\r
110 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2 (b)");
\r
116 Timer* t = new Timer();
\r
117 t->client = client;
\r
118 t->clientReference = clientReference;
\r
119 t->requestedTime.tv_sec = requestedTime;
\r
120 t->requestedTime.tv_nsec = requestedTimeNSEC;
\r
122 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
\r
123 timerList.push_back(t);
\r
124 resetThreadFlag = true;
\r
125 threadSignalNoLock();
\r
126 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
\r
129 logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
\r
134 int Timers::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)
\r
136 struct timespec currentTime;
\r
137 clock_gettime(CLOCK_REALTIME, ¤tTime);
\r
139 long int requestedTime;
\r
140 long int requestedTimeNSEC;
\r
142 requestedTime = currentTime.tv_sec + duration.tv_sec;
\r
143 requestedTimeNSEC = currentTime.tv_nsec + duration.tv_nsec;
\r
144 if (requestedTimeNSEC > 999999999)
\r
147 requestedTimeNSEC -= 1000000000;
\r
148 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
\r
151 return setTimer(client, clientReference, requestedTime, requestedTimeNSEC);
\r
154 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
\r
156 if (!initted) return 0;
\r
158 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
\r
160 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
\r
162 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
\r
163 TimerList::iterator i;
\r
164 Timer* currentTimer = NULL;
\r
165 for(i = timerList.begin(); i != timerList.end(); i++)
\r
168 //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);
\r
169 if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
\r
171 timerList.erase(i);
\r
172 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
\r
174 // At this point currentTimer is not in the list but might still be nextTimer in the thread
\r
177 if (i == timerList.end())
\r
180 logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);
\r
181 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
186 resetThreadFlag = true;
\r
187 threadSignalNoLock();
\r
188 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
195 void Timers::threadMethod()
\r
197 struct timespec nextTime;
\r
198 Timer* nextTimer = NULL;
\r
199 resetThreadFlag = true;
\r
205 if (resetThreadFlag)
\r
207 resetThreadFlag = false;
\r
209 // Work out the next Timer
\r
211 nextTime.tv_sec = 0;
\r
212 nextTime.tv_nsec = 0;
\r
215 TimerList::iterator i;
\r
216 Timer* currentTimer = NULL;
\r
217 for(i = timerList.begin(); i != timerList.end(); i++)
\r
222 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
223 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
224 nextTimer = currentTimer;
\r
228 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
\r
230 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
231 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
232 nextTimer = currentTimer;
\r
234 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
\r
236 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
\r
238 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
239 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
240 nextTimer = currentTimer;
\r
249 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
252 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
\r
253 threadWaitForSignalTimed(&nextTime); // FIXME does this work if the time is in the past?
\r
254 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
\r
256 // unlocks in the wait
\r
260 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
\r
261 threadWaitForSignal();
\r
262 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
\r
263 // unlocks in the wait
\r
266 // ok. we have been signalled or the time has run out
\r
267 // This only gets signalled if it is to reset or die
\r
269 // First check for die..
\r
270 threadCheckExit(); // exiting thread with mutex locked
\r
272 // Check for reset..
\r
273 // This can be caused by an addition or deletion to the list
\r
274 if (resetThreadFlag) continue;
\r
278 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
\r
280 // send this timer to the timer receiver, via the command message queue
\r
281 // so that the gui mutex is locked when it happens
\r
283 Message* m = new Message();
\r
285 m->to = nextTimer->client;
\r
286 m->message = Message::TIMER;
\r
287 m->parameter = nextTimer->clientReference;
\r
289 if (!Command::getInstance()->postMessageIfNotBusy(m))
\r
291 // GUI mutex was locked
\r
292 // abort this timer delivery - it might be trying to be deleted!
\r
295 // now unlock the timers mutex for a fraction of a second
\r
296 // in case the gui thread is waiting on the timers mutex
\r
298 //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");
\r
299 //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");
\r
300 usleep(10000); // 10ms - too long?
\r
301 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");
\r
303 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
\r
304 resetThreadFlag = true;
\r
308 // timer was delivered
\r
309 timerList.remove(nextTimer);
\r
312 resetThreadFlag = true;
\r
319 Avoiding deadlock using the timer class...
\r
323 timer condwait finishes
\r
324 timers is about to fire a timer
\r
325 timers locks timers-mutex
\r
327 user presses a button
\r
328 command locks gui-mutex
\r
330 timers tries to get gui-mutex
\r
332 view receives button
\r
333 view wants to delete itself
\r
334 view tries to deletetimer
\r
335 goes into delete timer
\r
336 waits on timers mutex
\r
343 timers tries to get gui mutex
\r
344 if mutex is locked already abort
\r
345 unlock timers mutex
\r
346 wait a fraction of time
\r
347 (allow other thread to lock timers mutex)
\r
349 set reset flag to recalculate
\r
350 - if timer has been cancelled next timer will be calced
\r
351 - if timer has not been cancelled it will be called next
\r