2 Copyright 2004-2007 Chris Tallon
4 This file is part of VOMP.
6 VOMP is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 VOMP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with VOMP; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "timerreceiver.h"
27 Timers* Timers::instance = NULL;
41 Timers* Timers::getInstance()
48 if (initted) return 0;
50 logger = Log::getInstance();
61 int Timers::shutdown()
63 if (!initted) return 0;
66 logger->log("Timers", Log::DEBUG, "Timers shutdown start");
70 TimerEvent* timerEvent = NULL;
71 TimerReceiver* client = NULL;
72 int clientReference = 0;
74 while(timerList.size())
77 timerEvent = timerList.front();
78 client = timerEvent->client;
79 clientReference = timerEvent->clientReference;
82 cancelTimer(client, clientReference);
85 logger->log("Timers", Log::DEBUG, "Timers shutdown end");
90 bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
92 if (!initted) return 0;
94 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
96 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
99 // Check that this timer is not already in the list
100 TimerList::iterator i;
101 TimerEvent* currentTimerEvent = NULL;
102 for(i = timerList.begin(); i != timerList.end(); i++)
104 currentTimerEvent = *i;
106 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
108 // Timer exists already, either waiting or running
110 currentTimerEvent->requestedTime.tv_sec = requestedTime;
111 currentTimerEvent->requestedTime.tv_nsec = requestedTimeNSEC;
113 if (currentTimerEvent->running)
115 // If this timerEvent is currently running, update the clocks and set the restart flag.
116 // Instead of being deleted in timerEventFinished it will be restarted
117 currentTimerEvent->restartAfterFinish = true;
118 // Don't need to resetThreadFlag because this timer isn't re-live yet
124 // A waiting timer has been edited
125 resetThreadFlag = true;
126 threadSignalNoLock();
133 // Timer did not exist already
135 TimerEvent* t = new TimerEvent();
137 t->clientReference = clientReference;
138 t->requestedTime.tv_sec = requestedTime;
139 t->requestedTime.tv_nsec = requestedTimeNSEC;
141 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
142 timerList.push_back(t);
143 resetThreadFlag = true;
144 threadSignalNoLock();
145 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
148 logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
153 bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
155 struct timespec currentTime;
157 getClockRealTime(¤tTime);
159 long int requestedTime;
160 long int requestedTimeNSEC;
162 requestedTime = currentTime.tv_sec + requestedSecs;
163 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
164 if (requestedTimeNSEC > 999999999)
167 requestedTimeNSEC -= 1000000000;
168 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
171 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
174 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
176 /* This method locks the timers mutex
177 Then one of three things can happen:
178 1. The TimerEvent is found, running = false. This means it hasn't started yet.
179 Delete the timer normally, set resetFlag
180 2. The TimerEvent is found, running = true. This means the timer is currently firing,
181 timercall on the client is being called.
182 a. Thread calling cancelTimer is an external thread: In this case, this thread
183 calling cancelTimer needs to unlock and wait for the timercall thread to get
184 back. (sleeps or signalling)
185 b. the timercall thread is calling cancelTimer. remove any restartAfterFinished
186 request, but otherwise ignore the request to cancelTimer because it has already
187 fired. The timercall thread will return to the calling code and eventually
188 terminate in threadEventFinished.
189 3. The TimerEvent is not found. Client error or the thread returned to
190 the Timers module in between client calling cancelTimer and cancelTimer actually
191 running. Do nothing, return normally.
193 By making sure there is no waiting timerevent, and no running timerevent, this ensures
194 that the program cannot segfault because a timer fired on a just deleted object.
198 if (!initted) return false;
200 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
206 TimerList::iterator i;
207 TimerEvent* currentTimerEvent = NULL;
208 for(i = timerList.begin(); i != timerList.end(); i++)
210 currentTimerEvent = *i;
211 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
217 if (i == timerList.end())
219 // Case 3, no timer found
225 // Timer found, Case 1 or 2
227 if (currentTimerEvent->running == false)
229 // Case 1. Just delete the timer and reset the thread.
231 delete currentTimerEvent;
232 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
233 resetThreadFlag = true;
234 threadSignalNoLock();
240 if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
243 // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
244 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
245 currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
250 // Case 2 a. For now, use polling with a 50ms delay.
251 // Don't delete a running timer.
252 // FIXME upgrade me to signalling
254 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
260 } // end of the big while loop
263 void Timers::timerEventFinished(TimerEvent* timerEvent)
265 // This function takes out the already timercall'd TimerEvent from the list
266 // Or resets it if restart flag is true
269 if (!initted) return;
272 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
274 for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
276 if (timerEvent != *i) continue;
278 if (timerEvent->restartAfterFinish)
280 logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
282 timerEvent->restartAfterFinish = false;
283 timerEvent->running = false;
284 resetThreadFlag = true;
285 threadSignalNoLock();
289 // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
291 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
297 // FIXME At this point, this should signal all threads waiting on cancelTimer
300 // Kill this thread, as it's the one started for the timer event
301 Thread_TYPE::threadSuicide();
304 void Timers::threadMethod()
306 struct timespec nextTime;
307 TimerEvent* nextTimer = NULL;
308 resetThreadFlag = true;
316 resetThreadFlag = false;
318 // Work out the next Timer
321 nextTime.tv_nsec = 0;
324 TimerList::iterator i;
325 TimerEvent* currentTimer = NULL;
326 for(i = timerList.begin(); i != timerList.end(); i++)
329 if (currentTimer->running) continue; // has already been timercall'd
333 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
334 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
335 nextTimer = currentTimer;
339 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
341 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
342 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
343 nextTimer = currentTimer;
345 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
347 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
349 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
350 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
351 nextTimer = currentTimer;
360 //## 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);
363 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
364 threadWaitForSignalTimed(&nextTime);
365 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
367 // unlocks in the wait
371 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
372 threadWaitForSignal();
373 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
374 // unlocks in the wait
377 // Mutex locked again here by exit of wait or timedwait above
379 // ok. we have been signalled or the time has run out
380 // This only gets signalled if it is to reset or die
382 // First check for die..
383 threadCheckExit(); // exiting thread with mutex locked
386 // This can be caused by an addition or deletion to the list
387 if (resetThreadFlag || (nextTimer == NULL)) continue;
391 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
393 nextTimer->run(); // sets timerevent to running and starts it
394 resetThreadFlag = true; // find a new timer to wait on
402 TimerEvent::TimerEvent()
405 restartAfterFinish = false;
408 requestedTime.tv_sec = 0;
409 requestedTime.tv_nsec = 0;
412 void TimerEvent::threadMethod()
414 Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
415 client->timercall(clientReference);
416 Timers::getInstance()->timerEventFinished(this); // does not return
419 void TimerEvent::run()