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
23 Timers* Timers::instance = NULL;
37 Timers* Timers::getInstance()
44 if (initted) return 0;
46 logger = Log::getInstance();
48 threadLock(); // lock here, the thread loop will unlock and wait
58 int Timers::shutdown()
60 if (!initted) return 0;
63 logger->log("Timers", Log::DEBUG, "Timers shutdown start");
67 TimerEvent* timerEvent = NULL;
68 TimerReceiver* client = NULL;
69 int clientReference = 0;
71 while(timerList.size())
74 timerEvent = timerList.front();
75 client = timerEvent->client;
76 clientReference = timerEvent->clientReference;
79 cancelTimer(client, clientReference);
82 logger->log("Timers", Log::DEBUG, "Timers shutdown end");
87 bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
89 if (!initted) return 0;
91 logger->log("Timers", Log::DEBUG, "Starting set timer 1");
93 //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
96 // Check that this timer is not already in the list
97 TimerList::iterator i;
98 TimerEvent* currentTimerEvent = NULL;
99 for(i = timerList.begin(); i != timerList.end(); i++)
101 currentTimerEvent = *i;
103 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
105 // Timer exists already, either waiting or running
107 currentTimerEvent->requestedTime.tv_sec = requestedTime;
108 currentTimerEvent->requestedTime.tv_nsec = requestedTimeNSEC;
110 if (currentTimerEvent->running)
112 // If this timerEvent is currently running, update the clocks and set the restart flag.
113 // Instead of being deleted in timerEventFinished it will be restarted
114 currentTimerEvent->restartAfterFinish = true;
115 // Don't need to resetThreadFlag because this timer isn't re-live yet
121 // A waiting timer has been edited
122 resetThreadFlag = true;
123 threadSignalNoLock();
130 // Timer did not exist already
132 TimerEvent* t = new TimerEvent();
134 t->clientReference = clientReference;
135 t->requestedTime.tv_sec = requestedTime;
136 t->requestedTime.tv_nsec = requestedTimeNSEC;
138 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
139 timerList.push_back(t);
140 resetThreadFlag = true;
141 threadSignalNoLock();
142 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
145 logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
150 bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
152 struct timespec currentTime;
155 clock_gettime(CLOCK_REALTIME, ¤tTime);
160 GetSystemTime(&systime);
161 SystemTimeToFileTime(&systime,(FILETIME*)&filetime);
162 currentTime.tv_sec=(filetime-WINDOWS_TIME_BASE_OFFSET)/(10*1000*1000);
163 //#error "Hier gibt was zu tun!"
164 currentTime.tv_nsec=((filetime-WINDOWS_TIME_BASE_OFFSET)%(10*1000*1000))*100;
167 long int requestedTime;
168 long int requestedTimeNSEC;
170 requestedTime = currentTime.tv_sec + requestedSecs;
171 requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
172 if (requestedTimeNSEC > 999999999)
175 requestedTimeNSEC -= 1000000000;
176 logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
179 return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
182 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
184 /* This method locks the timers mutex
185 Then one of three things can happen:
186 1. The TimerEvent is found, running = false. This means it hasn't started yet.
187 Delete the timer normally, set resetFlag
188 2. The TimerEvent is found, running = true. This means the timer is currently firing,
189 timercall on the client is being called.
190 a. Thread calling cancelTimer is an external thread: In this case, this thread
191 calling cancelTimer needs to unlock and wait for the timercall thread to get
192 back. (sleeps or signalling)
193 b. the timercall thread is calling cancelTimer. remove any restartAfterFinished
194 request, but otherwise ignore the request to cancelTimer because it has already
195 fired. The timercall thread will return to the calling code and eventually
196 terminate in threadEventFinished.
197 3. The TimerEvent is not found. Client error or the thread returned to
198 the Timers module in between client calling cancelTimer and cancelTimer actually
199 running. Do nothing, return normally.
201 By making sure there is no waiting timerevent, and no running timerevent, this ensures
202 that the program cannot segfault because a timer fired on a just deleted object.
206 if (!initted) return false;
208 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
214 TimerList::iterator i;
215 TimerEvent* currentTimerEvent = NULL;
216 for(i = timerList.begin(); i != timerList.end(); i++)
218 currentTimerEvent = *i;
219 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
225 if (i == timerList.end())
227 // Case 3, no timer found
233 // Timer found, Case 1 or 2
235 if (currentTimerEvent->running == false)
237 // Case 1. Just delete the timer and reset the thread.
239 delete currentTimerEvent;
240 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
241 resetThreadFlag = true;
242 threadSignalNoLock();
248 if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
251 // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
252 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
253 currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
258 // Case 2 a. For now, use polling with a 50ms delay.
259 // Don't delete a running timer.
260 // FIXME upgrade me to signalling
262 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
268 } // end of the big while loop
271 void Timers::timerEventFinished(TimerEvent* timerEvent)
273 // This function takes out the already timercall'd TimerEvent from the list
274 // Or resets it if restart flag is true
277 if (!initted) return;
280 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
282 for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
284 if (timerEvent != *i) continue;
286 if (timerEvent->restartAfterFinish)
288 logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
290 timerEvent->restartAfterFinish = false;
291 timerEvent->running = false;
292 resetThreadFlag = true;
293 threadSignalNoLock();
297 // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
299 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
305 // FIXME At this point, this should signal all threads waiting on cancelTimer
308 // Kill this thread, as it's the one started for the timer event
309 Thread_TYPE::threadSuicide();
312 void Timers::threadMethod()
314 struct timespec nextTime;
315 TimerEvent* nextTimer = NULL;
316 resetThreadFlag = true;
324 resetThreadFlag = false;
326 // Work out the next Timer
329 nextTime.tv_nsec = 0;
332 TimerList::iterator i;
333 TimerEvent* currentTimer = NULL;
334 for(i = timerList.begin(); i != timerList.end(); i++)
337 if (currentTimer->running) continue; // has already been timercall'd
341 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
342 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
343 nextTimer = currentTimer;
347 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
349 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
350 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
351 nextTimer = currentTimer;
353 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
355 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
357 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
358 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
359 nextTimer = currentTimer;
368 //## 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);
371 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
372 threadWaitForSignalTimed(&nextTime);
373 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
375 // unlocks in the wait
379 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
380 threadWaitForSignal();
381 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
382 // unlocks in the wait
385 // Mutex locked again here by exit of wait or timedwait above
387 // ok. we have been signalled or the time has run out
388 // This only gets signalled if it is to reset or die
390 // First check for die..
391 threadCheckExit(); // exiting thread with mutex locked
394 // This can be caused by an addition or deletion to the list
395 if (resetThreadFlag || (nextTimer == NULL)) continue;
399 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
401 nextTimer->run(); // sets timerevent to running and starts it
402 resetThreadFlag = true; // find a new timer to wait on
410 TimerEvent::TimerEvent()
413 restartAfterFinish = false;
416 requestedTime.tv_sec = 0;
417 requestedTime.tv_nsec = 0;
420 void TimerEvent::threadMethod()
422 Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
423 client->timercall(clientReference);
424 Timers::getInstance()->timerEventFinished(this); // does not return
427 void TimerEvent::run()