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::setTimer(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::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)
\r
132 struct timespec currentTime;
\r
133 clock_gettime(CLOCK_REALTIME, ¤tTime);
\r
135 long int requestedTime;
\r
136 long int requestedTimeNSEC;
\r
138 requestedTime = currentTime.tv_sec + duration.tv_sec;
\r
139 requestedTimeNSEC = currentTime.tv_nsec + duration.tv_nsec;
\r
140 if (requestedTimeNSEC > 999999999)
\r
143 requestedTimeNSEC -= 1000000000;
\r
144 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
\r
147 return setTimer(client, clientReference, requestedTime, requestedTimeNSEC);
\r
150 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
\r
152 if (!initted) return 0;
\r
154 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
\r
156 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
\r
158 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
\r
159 TimerList::iterator i;
\r
160 Timer* currentTimer = NULL;
\r
161 for(i = timerList.begin(); i != timerList.end(); i++)
\r
164 //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);
\r
165 if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
\r
167 timerList.erase(i);
\r
168 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
\r
170 // At this point currentTimer is not in the list but might still be nextTimer in the thread
\r
173 if (i == timerList.end())
\r
176 logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);
\r
177 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
182 resetThreadFlag = true;
\r
183 threadSignalNoLock();
\r
184 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
191 void Timers::threadMethod()
\r
193 struct timespec nextTime;
\r
194 Timer* nextTimer = NULL;
\r
195 resetThreadFlag = true;
\r
201 if (resetThreadFlag)
\r
203 resetThreadFlag = false;
\r
205 // Work out the next Timer
\r
207 nextTime.tv_sec = 0;
\r
208 nextTime.tv_nsec = 0;
\r
211 TimerList::iterator i;
\r
212 Timer* currentTimer = NULL;
\r
213 for(i = timerList.begin(); i != timerList.end(); i++)
\r
218 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
219 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
220 nextTimer = currentTimer;
\r
224 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
\r
226 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
227 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
228 nextTimer = currentTimer;
\r
230 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
\r
232 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
\r
234 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
235 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
236 nextTimer = currentTimer;
\r
245 //## 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
248 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
\r
249 threadWaitForSignalTimed(&nextTime);
\r
250 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
\r
252 // unlocks in the wait
\r
256 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
\r
257 threadWaitForSignal();
\r
258 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
\r
259 // unlocks in the wait
\r
262 // ok. we have been signalled or the time has run out
\r
263 // This only gets signalled if it is to reset or die
\r
265 // First check for die..
\r
266 threadCheckExit(); // exiting thread with mutex locked
\r
268 // Check for reset..
\r
269 // This can be caused by an addition or deletion to the list
\r
270 if (resetThreadFlag) continue;
\r
274 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
\r
276 // send this timer to the timer receiver, via the command message queue
\r
277 // so that the gui mutex is locked when it happens
\r
279 Message* m = new Message(); // Timer call, must be injected into master mutex (this is generated outside the mutex)
\r
281 m->to = nextTimer->client;
\r
282 m->message = Message::TIMER;
\r
283 m->parameter = nextTimer->clientReference;
\r
285 if (!Command::getInstance()->postMessageIfNotBusy(m))
\r
287 // GUI mutex was locked
\r
288 // abort this timer delivery - it might be trying to be deleted!
\r
291 // now unlock the timers mutex for a fraction of a second
\r
292 // in case the gui thread is waiting on the timers mutex
\r
294 //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");
\r
295 //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");
\r
296 usleep(10000); // 10ms - too long?
\r
297 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");
\r
299 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
\r
300 resetThreadFlag = true;
\r
304 // timer was delivered
\r
305 timerList.remove(nextTimer);
\r
308 resetThreadFlag = true;
\r
315 Avoiding deadlock using the timer class...
\r
319 timer condwait finishes
\r
320 timers is about to fire a timer
\r
321 timers locks timers-mutex
\r
323 user presses a button
\r
324 command locks gui-mutex
\r
326 timers tries to get gui-mutex
\r
328 view receives button
\r
329 view wants to delete itself
\r
330 view tries to deletetimer
\r
331 goes into delete timer
\r
332 waits on timers mutex
\r
339 timers tries to get gui mutex
\r
340 if mutex is locked already abort
\r
341 unlock timers mutex
\r
342 wait a fraction of time
\r
343 (allow other thread to lock timers mutex)
\r
345 set reset flag to recalculate
\r
346 - if timer has been cancelled next timer will be calced
\r
347 - if timer has not been cancelled it will be called next
\r