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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "timerreceiver.h"
27 Timers* Timers::instance = NULL;
41 Timers* Timers::getInstance()
48 if (initted) return 0;
50 logger = Log::getInstance();
52 threadLock(); // lock here, the thread loop will unlock and wait
62 int Timers::shutdown()
64 if (!initted) return 0;
67 logger->log("Timers", Log::DEBUG, "Timers shutdown start");
71 TimerEvent* timerEvent = NULL;
72 TimerReceiver* client = NULL;
73 int clientReference = 0;
75 while(timerList.size())
78 timerEvent = timerList.front();
79 client = timerEvent->client;
80 clientReference = timerEvent->clientReference;
83 cancelTimer(client, clientReference);
86 logger->log("Timers", Log::DEBUG, "Timers shutdown end");
91 bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
93 if (!initted) return 0;
95 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
97 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
100 // Check that this timer is not already in the list
101 TimerList::iterator i;
102 TimerEvent* currentTimerEvent = NULL;
103 for(i = timerList.begin(); i != timerList.end(); i++)
105 currentTimerEvent = *i;
107 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
109 // Timer exists already, either waiting or running
111 currentTimerEvent->requestedTime.tv_sec = requestedTime;
112 currentTimerEvent->requestedTime.tv_nsec = requestedTimeNSEC;
114 if (currentTimerEvent->running)
116 // If this timerEvent is currently running, update the clocks and set the restart flag.
117 // Instead of being deleted in timerEventFinished it will be restarted
118 currentTimerEvent->restartAfterFinish = true;
119 // Don't need to resetThreadFlag because this timer isn't re-live yet
125 // A waiting timer has been edited
126 resetThreadFlag = true;
127 threadSignalNoLock();
134 // Timer did not exist already
136 TimerEvent* t = new TimerEvent();
138 t->clientReference = clientReference;
139 t->requestedTime.tv_sec = requestedTime;
140 t->requestedTime.tv_nsec = requestedTimeNSEC;
142 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
143 timerList.push_back(t);
144 resetThreadFlag = true;
145 threadSignalNoLock();
146 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
149 logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
154 bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
156 struct timespec currentTime;
159 clock_gettime(CLOCK_REALTIME, ¤tTime);
164 GetSystemTime(&systime);
165 SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
166 currentTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
167 //#error "Hier gibt was zu tun!"
168 currentTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
171 long int requestedTime;
172 long int requestedTimeNSEC;
174 requestedTime = currentTime.tv_sec + requestedSecs;
175 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
176 if (requestedTimeNSEC > 999999999)
179 requestedTimeNSEC -= 1000000000;
180 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
183 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
186 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
188 /* This method locks the timers mutex
189 Then one of three things can happen:
190 1. The TimerEvent is found, running = false. This means it hasn't started yet.
191 Delete the timer normally, set resetFlag
192 2. The TimerEvent is found, running = true. This means the timer is currently firing,
193 timercall on the client is being called.
194 a. Thread calling cancelTimer is an external thread: In this case, this thread
195 calling cancelTimer needs to unlock and wait for the timercall thread to get
196 back. (sleeps or signalling)
197 b. the timercall thread is calling cancelTimer. remove any restartAfterFinished
198 request, but otherwise ignore the request to cancelTimer because it has already
199 fired. The timercall thread will return to the calling code and eventually
200 terminate in threadEventFinished.
201 3. The TimerEvent is not found. Client error or the thread returned to
202 the Timers module in between client calling cancelTimer and cancelTimer actually
203 running. Do nothing, return normally.
205 By making sure there is no waiting timerevent, and no running timerevent, this ensures
206 that the program cannot segfault because a timer fired on a just deleted object.
210 if (!initted) return false;
212 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
218 TimerList::iterator i;
219 TimerEvent* currentTimerEvent = NULL;
220 for(i = timerList.begin(); i != timerList.end(); i++)
222 currentTimerEvent = *i;
223 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
229 if (i == timerList.end())
231 // Case 3, no timer found
237 // Timer found, Case 1 or 2
239 if (currentTimerEvent->running == false)
241 // Case 1. Just delete the timer and reset the thread.
243 delete currentTimerEvent;
244 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
245 resetThreadFlag = true;
246 threadSignalNoLock();
252 if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
255 // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
256 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
257 currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
262 // Case 2 a. For now, use polling with a 50ms delay.
263 // Don't delete a running timer.
264 // FIXME upgrade me to signalling
266 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
272 } // end of the big while loop
275 void Timers::timerEventFinished(TimerEvent* timerEvent)
277 // This function takes out the already timercall'd TimerEvent from the list
278 // Or resets it if restart flag is true
281 if (!initted) return;
284 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
286 for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
288 if (timerEvent != *i) continue;
290 if (timerEvent->restartAfterFinish)
292 logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
294 timerEvent->restartAfterFinish = false;
295 timerEvent->running = false;
296 resetThreadFlag = true;
297 threadSignalNoLock();
301 // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
303 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
309 // FIXME At this point, this should signal all threads waiting on cancelTimer
312 // Kill this thread, as it's the one started for the timer event
313 Thread_TYPE::threadSuicide();
316 void Timers::threadMethod()
318 struct timespec nextTime;
319 TimerEvent* nextTimer = NULL;
320 resetThreadFlag = true;
328 resetThreadFlag = false;
330 // Work out the next Timer
333 nextTime.tv_nsec = 0;
336 TimerList::iterator i;
337 TimerEvent* currentTimer = NULL;
338 for(i = timerList.begin(); i != timerList.end(); i++)
341 if (currentTimer->running) continue; // has already been timercall'd
345 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
346 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
347 nextTimer = currentTimer;
351 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
353 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
354 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
355 nextTimer = currentTimer;
357 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
359 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
361 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
362 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
363 nextTimer = currentTimer;
372 //## 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);
375 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
376 threadWaitForSignalTimed(&nextTime);
377 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
379 // unlocks in the wait
383 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
384 threadWaitForSignal();
385 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
386 // unlocks in the wait
389 // Mutex locked again here by exit of wait or timedwait above
391 // ok. we have been signalled or the time has run out
392 // This only gets signalled if it is to reset or die
394 // First check for die..
395 threadCheckExit(); // exiting thread with mutex locked
398 // This can be caused by an addition or deletion to the list
399 if (resetThreadFlag || (nextTimer == NULL)) continue;
403 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
405 nextTimer->run(); // sets timerevent to running and starts it
406 resetThreadFlag = true; // find a new timer to wait on
414 TimerEvent::TimerEvent()
417 restartAfterFinish = false;
420 requestedTime.tv_sec = 0;
421 requestedTime.tv_nsec = 0;
424 void TimerEvent::threadMethod()
426 Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
427 client->timercall(clientReference);
428 Timers::getInstance()->timerEventFinished(this); // does not return
431 void TimerEvent::run()