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;
158 clock_gettime(CLOCK_REALTIME, ¤tTime);
163 GetSystemTime(&systime);
164 SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
165 currentTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
166 //#error "Hier gibt was zu tun!"
167 currentTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
170 long int requestedTime;
171 long int requestedTimeNSEC;
173 requestedTime = currentTime.tv_sec + requestedSecs;
174 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
175 if (requestedTimeNSEC > 999999999)
178 requestedTimeNSEC -= 1000000000;
179 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
182 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
185 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
187 /* This method locks the timers mutex
188 Then one of three things can happen:
189 1. The TimerEvent is found, running = false. This means it hasn't started yet.
190 Delete the timer normally, set resetFlag
191 2. The TimerEvent is found, running = true. This means the timer is currently firing,
192 timercall on the client is being called.
193 a. Thread calling cancelTimer is an external thread: In this case, this thread
194 calling cancelTimer needs to unlock and wait for the timercall thread to get
195 back. (sleeps or signalling)
196 b. the timercall thread is calling cancelTimer. remove any restartAfterFinished
197 request, but otherwise ignore the request to cancelTimer because it has already
198 fired. The timercall thread will return to the calling code and eventually
199 terminate in threadEventFinished.
200 3. The TimerEvent is not found. Client error or the thread returned to
201 the Timers module in between client calling cancelTimer and cancelTimer actually
202 running. Do nothing, return normally.
204 By making sure there is no waiting timerevent, and no running timerevent, this ensures
205 that the program cannot segfault because a timer fired on a just deleted object.
209 if (!initted) return false;
211 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
217 TimerList::iterator i;
218 TimerEvent* currentTimerEvent = NULL;
219 for(i = timerList.begin(); i != timerList.end(); i++)
221 currentTimerEvent = *i;
222 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
228 if (i == timerList.end())
230 // Case 3, no timer found
236 // Timer found, Case 1 or 2
238 if (currentTimerEvent->running == false)
240 // Case 1. Just delete the timer and reset the thread.
242 delete currentTimerEvent;
243 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
244 resetThreadFlag = true;
245 threadSignalNoLock();
251 if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
254 // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
255 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
256 currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
261 // Case 2 a. For now, use polling with a 50ms delay.
262 // Don't delete a running timer.
263 // FIXME upgrade me to signalling
265 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
271 } // end of the big while loop
274 void Timers::timerEventFinished(TimerEvent* timerEvent)
276 // This function takes out the already timercall'd TimerEvent from the list
277 // Or resets it if restart flag is true
280 if (!initted) return;
283 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
285 for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
287 if (timerEvent != *i) continue;
289 if (timerEvent->restartAfterFinish)
291 logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
293 timerEvent->restartAfterFinish = false;
294 timerEvent->running = false;
295 resetThreadFlag = true;
296 threadSignalNoLock();
300 // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
302 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
308 // FIXME At this point, this should signal all threads waiting on cancelTimer
311 // Kill this thread, as it's the one started for the timer event
312 Thread_TYPE::threadSuicide();
315 void Timers::threadMethod()
317 struct timespec nextTime;
318 TimerEvent* nextTimer = NULL;
319 resetThreadFlag = true;
327 resetThreadFlag = false;
329 // Work out the next Timer
332 nextTime.tv_nsec = 0;
335 TimerList::iterator i;
336 TimerEvent* currentTimer = NULL;
337 for(i = timerList.begin(); i != timerList.end(); i++)
340 if (currentTimer->running) continue; // has already been timercall'd
344 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
345 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
346 nextTimer = currentTimer;
350 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
352 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
353 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
354 nextTimer = currentTimer;
356 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
358 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
360 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
361 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
362 nextTimer = currentTimer;
371 //## 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);
374 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
375 threadWaitForSignalTimed(&nextTime);
376 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
378 // unlocks in the wait
382 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
383 threadWaitForSignal();
384 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
385 // unlocks in the wait
388 // Mutex locked again here by exit of wait or timedwait above
390 // ok. we have been signalled or the time has run out
391 // This only gets signalled if it is to reset or die
393 // First check for die..
394 threadCheckExit(); // exiting thread with mutex locked
397 // This can be caused by an addition or deletion to the list
398 if (resetThreadFlag || (nextTimer == NULL)) continue;
402 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
404 nextTimer->run(); // sets timerevent to running and starts it
405 resetThreadFlag = true; // find a new timer to wait on
413 TimerEvent::TimerEvent()
416 restartAfterFinish = false;
419 requestedTime.tv_sec = 0;
420 requestedTime.tv_nsec = 0;
423 void TimerEvent::threadMethod()
425 Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
426 client->timercall(clientReference);
427 Timers::getInstance()->timerEventFinished(this); // does not return
430 void TimerEvent::run()