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, time_t requestedTime)
\r
89 if (!initted) return 0;
\r
91 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
\r
93 Timer* t = new Timer();
\r
95 t->clientReference = clientReference;
\r
96 t->requestedTime.tv_sec = requestedTime;
\r
97 t->requestedTime.tv_nsec = 0;
\r
99 logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
\r
101 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
\r
102 timerList.push_back(t);
\r
103 resetThreadFlag = true;
\r
104 threadSignalNoLock();
\r
105 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
\r
108 logger->log("Timers", Log::DEBUG, "1 Have set timer for %p ref %i", client, clientReference);
\r
113 int Timers::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)
\r
115 if (!initted) return 0;
\r
117 logger->log("Timers", Log::DEBUG, "Starting set timer 2");
\r
119 Timer* t = new Timer();
\r
120 t->client = client;
\r
121 t->clientReference = clientReference;
\r
123 struct timespec currentTime;
\r
124 clock_gettime(CLOCK_REALTIME, ¤tTime);
\r
126 t->requestedTime.tv_sec = currentTime.tv_sec + duration.tv_sec;
\r
127 t->requestedTime.tv_nsec = currentTime.tv_nsec + duration.tv_nsec;
\r
128 if (t->requestedTime.tv_nsec > 999999999)
\r
130 ++t->requestedTime.tv_sec;
\r
131 t->requestedTime.tv_nsec -= 1000000000;
\r
132 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
\r
135 logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 3");
\r
137 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 3");
\r
138 timerList.push_back(t);
\r
139 resetThreadFlag = true;
\r
140 threadSignalNoLock();
\r
141 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 3");
\r
144 logger->log("Timers", Log::DEBUG, "2 Have set timer for %p ref %i", client, clientReference);
\r
149 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
\r
151 if (!initted) return 0;
\r
153 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
\r
155 logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
\r
157 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
\r
158 TimerList::iterator i;
\r
159 Timer* currentTimer = NULL;
\r
160 for(i = timerList.begin(); i != timerList.end(); i++)
\r
163 logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);
\r
164 if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
\r
166 timerList.erase(i);
\r
167 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
\r
169 // At this point currentTimer is not in the list but might still be nextTimer in the thread
\r
172 if (i == timerList.end())
\r
175 logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);
\r
176 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
181 resetThreadFlag = true;
\r
182 threadSignalNoLock();
\r
183 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
\r
190 void Timers::threadMethod()
\r
192 struct timespec nextTime;
\r
193 Timer* nextTimer = NULL;
\r
194 resetThreadFlag = true;
\r
200 if (resetThreadFlag)
\r
202 resetThreadFlag = false;
\r
204 // Work out the next Timer
\r
206 nextTime.tv_sec = 0;
\r
207 nextTime.tv_nsec = 0;
\r
210 TimerList::iterator i;
\r
211 Timer* currentTimer = NULL;
\r
212 for(i = timerList.begin(); i != timerList.end(); i++)
\r
217 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
218 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
219 nextTimer = currentTimer;
\r
223 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
\r
225 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
226 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
227 nextTimer = currentTimer;
\r
229 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
\r
231 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
\r
233 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
\r
234 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
\r
235 nextTimer = currentTimer;
\r
244 logger->log("Timers", Log::DEBUG, "List size: %i. nextTimer: %p. nextTime.tv_sec: %li. nextTime.tv_nsec: %li", timerList.size(), nextTimer, nextTime.tv_sec, nextTime.tv_nsec);
\r
247 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
\r
248 threadWaitForSignalTimed(&nextTime); // FIXME does this work if the time is in the past?
\r
249 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
\r
250 logger->log("Timers", Log::DEBUG, "FIXME CHECK does waitforsignaltimed work for time < now?");
\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();
\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); // 5ms - 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