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
251 // unlocks in the wait
\r
255 logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
\r
256 threadWaitForSignal();
\r
257 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
\r
258 // unlocks in the wait
\r
261 // ok. we have been signalled or the time has run out
\r
262 // This only gets signalled if it is to reset or die
\r
264 // First check for die..
\r
265 threadCheckExit(); // exiting thread with mutex locked
\r
267 // Check for reset..
\r
268 // This can be caused by an addition or deletion to the list
\r
269 if (resetThreadFlag) continue;
\r
273 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
\r
275 // send this timer to the timer receiver, via the command message queue
\r
276 // so that the gui mutex is locked when it happens
\r
278 Message* m = new Message();
\r
280 m->to = nextTimer->client;
\r
281 m->message = Message::TIMER;
\r
282 m->parameter = nextTimer->clientReference;
\r
284 if (!Command::getInstance()->postMessageIfNotBusy(m))
\r
286 // GUI mutex was locked
\r
287 // abort this timer delivery - it might be trying to be deleted!
\r
290 // now unlock the timers mutex for a fraction of a second
\r
291 // in case the gui thread is waiting on the timers mutex
\r
293 logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");
\r
294 printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");
\r
295 usleep(10000); // 5ms - too long?
\r
296 logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");
\r
298 logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
\r
299 resetThreadFlag = true;
\r
303 // timer was delivered
\r
304 timerList.remove(nextTimer);
\r
307 resetThreadFlag = true;
\r
314 Avoiding deadlock using the timer class...
\r
318 timer condwait finishes
\r
319 timers is about to fire a timer
\r
320 timers locks timers-mutex
\r
322 user presses a button
\r
323 command locks gui-mutex
\r
325 timers tries to get gui-mutex
\r
327 view receives button
\r
328 view wants to delete itself
\r
329 view tries to deletetimer
\r
330 goes into delete timer
\r
331 waits on timers mutex
\r
338 timers tries to get gui mutex
\r
339 if mutex is locked already abort
\r
340 unlock timers mutex
\r
341 wait a fraction of time
\r
342 (allow other thread to lock timers mutex)
\r
344 set reset flag to recalculate
\r
345 - if timer has been cancelled next timer will be calced
\r
346 - if timer has not been cancelled it will be called next
\r