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 In this case, this thread calling cancelTimer needs to unlock and wait for the
191 timercall thread to get back. (sleeps or signalling)
192 3. The TimerEvent is not found. Client error or the thread returned to
193 the Timers module in between client calling cancelTimer and cancelTimer actually
194 running. Do nothing, return normally.
196 By making sure there is no waiting timerevent, and no running timerevent, this ensures
197 that the program cannot segfault because a timer fired on a just deleted object.
201 if (!initted) return false;
203 logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
209 TimerList::iterator i;
210 TimerEvent* currentTimerEvent = NULL;
211 for(i = timerList.begin(); i != timerList.end(); i++)
213 currentTimerEvent = *i;
214 if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
220 if (i == timerList.end())
222 // Case 3, no timer found
228 // Timer found, Case 1 or 2
230 if (currentTimerEvent->running == false)
232 // Case 1. Just delete the timer and reset the thread.
234 delete currentTimerEvent;
235 logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
236 resetThreadFlag = true;
237 threadSignalNoLock();
243 // Case 2. For now, use polling with a 50ms delay.
244 // Don't delete a running timer.
245 // FIXME upgrade me to signalling
247 logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
253 } // end of the big while loop
256 void Timers::timerEventFinished(TimerEvent* timerEvent)
258 // This function takes out the already timercall'd TimerEvent from the list
259 // Or resets it if restart flag is true
262 if (!initted) return;
265 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
267 for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
269 if (timerEvent != *i) continue;
271 if (timerEvent->restartAfterFinish)
273 logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
275 timerEvent->restartAfterFinish = false;
276 timerEvent->running = false;
277 resetThreadFlag = true;
278 threadSignalNoLock();
282 // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
284 logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
290 // FIXME At this point, this should signal all threads waiting on cancelTimer
293 // Kill this thread, as it's the one started for the timer event
294 Thread_TYPE::threadSuicide();
297 void Timers::threadMethod()
299 struct timespec nextTime;
300 TimerEvent* nextTimer = NULL;
301 resetThreadFlag = true;
309 resetThreadFlag = false;
311 // Work out the next Timer
314 nextTime.tv_nsec = 0;
317 TimerList::iterator i;
318 TimerEvent* currentTimer = NULL;
319 for(i = timerList.begin(); i != timerList.end(); i++)
322 if (currentTimer->running) continue; // has already been timercall'd
326 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
327 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
328 nextTimer = currentTimer;
332 if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
334 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
335 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
336 nextTimer = currentTimer;
338 else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
340 if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
342 nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
343 nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
344 nextTimer = currentTimer;
353 //## 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);
356 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
357 threadWaitForSignalTimed(&nextTime);
358 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
360 // unlocks in the wait
364 //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
365 threadWaitForSignal();
366 //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
367 // unlocks in the wait
370 // Mutex locked again here by exit of wait or timedwait above
372 // ok. we have been signalled or the time has run out
373 // This only gets signalled if it is to reset or die
375 // First check for die..
376 threadCheckExit(); // exiting thread with mutex locked
379 // This can be caused by an addition or deletion to the list
380 if (resetThreadFlag || (nextTimer == NULL)) continue;
384 Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
386 nextTimer->run(); // sets timerevent to running and starts it
387 resetThreadFlag = true; // find a new timer to wait on
395 TimerEvent::TimerEvent()
398 restartAfterFinish = false;
401 requestedTime.tv_sec = 0;
402 requestedTime.tv_nsec = 0;
405 void TimerEvent::threadMethod()
407 Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
408 client->timercall(clientReference);
409 Timers::getInstance()->timerEventFinished(this); // does not return
412 void TimerEvent::run()