1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // Auxiliary Software class library
6 //
7 // based on the work of JasonB
8 // enhanced by Dark Charlie and Albert Zeyer
9 //
10 // code under LGPL
11 //
12 /////////////////////////////////////////
13
14
15 // Timer
16 // Created 12/11/01
17 // Jason Boettcher
18
19
20 #include <list>
21 #include "ThreadPool.h"
22 #include <time.h>
23 #include <cassert>
24 #include "Timer.h"
25 #include "Debug.h"
26 #include "InputEvents.h"
27
28
29 TimeCounter timeCounter;
30
31 int Frames = 0;
32 AbsTime OldFPSTime = AbsTime();
33 int Fps = 0;
34
35
36 ///////////////////
37 // Get the frames per second count
38 // Should be called once per frame
GetFPS()39 int GetFPS()
40 {
41 Frames++;
42
43 TimeDiff dt = GetTime() - OldFPSTime;
44 if(dt >= 1.0f) {
45 OldFPSTime = GetTime();
46 Fps = (int)( (float)Frames / dt.seconds() );
47 Frames = 0;
48 }
49
50 return Fps;
51 }
52
53 int Frames_MinFPS = 0;
54 AbsTime OldFPSTime_MinFPS = AbsTime();
55 AbsTime PrevFrameTime_MinFPS = AbsTime();
56 TimeDiff MaxFrameTime_MinFPS = TimeDiff();
57 int Fps_MinFPS = 0;
58
59 ///////////////////
60 // Get the minimal frames per second count ( the slowest frame )
61 // Should be called once per frame
GetMinFPS()62 int GetMinFPS()
63 {
64 Frames_MinFPS++;
65 AbsTime ms = GetTime();
66 if( ms - OldFPSTime_MinFPS >= 1.0f )
67 {
68 OldFPSTime_MinFPS = ms;
69
70 if( MaxFrameTime_MinFPS > 0.00000001f )
71 Fps_MinFPS = (int)( 1.0f / MaxFrameTime_MinFPS.seconds() );
72 if( Fps_MinFPS > Fps )
73 Fps_MinFPS = Fps;
74
75 MaxFrameTime_MinFPS = ms - PrevFrameTime_MinFPS;
76 Frames_MinFPS = 0;
77 }
78
79 if( MaxFrameTime_MinFPS < ms - PrevFrameTime_MinFPS )
80 MaxFrameTime_MinFPS = ms - PrevFrameTime_MinFPS;
81
82 PrevFrameTime_MinFPS = ms;
83
84 return Fps_MinFPS;
85 }
86
87 ///////////////////
88 // Get the actual time
GetDateTime()89 std::string GetDateTime()
90 {
91 char cTime[64];
92 cTime[0]= '\0';
93 time_t t;
94 time(&t);
95 struct tm* tp;
96 tp = localtime(&t);
97 if (tp)
98 strftime(cTime, 26, "%Y-%m-%d-%a-%H-%M-%S", tp);
99 cTime[63] = '\0';
100 return cTime;
101 }
102
103
104
105
106
107
108
109
110 // -----------------------------------
111 // Timer class
112 // HINT: the timer is not exact, do not use it for any exact timing, like ingame simulation
113
114
115
116 struct InternTimerEventData {
117 TimerData* data; // for intern struct TimerData
118 bool lastEvent;
InternTimerEventDataInternTimerEventData119 InternTimerEventData(TimerData* d, bool le) : data(d), lastEvent(le) {}
120 };
121 Event<InternTimerEventData> onInternTimerSignal;
122 static bool timerSystem_inited = false;
123
124
125 static SDL_mutex* TimerGlobalMutex = NULL;
126
127 struct TimerData;
128 static void RemoveTimerFromGlobalList(TimerData *data);
129
130
131 // Timer data, contains almost the same info as the timer class
132 struct TimerData {
133 Timer* timer;
134 std::string name;
135 Event<Timer::EventData>::HandlerList onTimerHandler;
136 void* userData;
137 Uint32 interval;
138 bool once;
139 bool quitSignal;
140 SDL_cond* quitCond;
141 SDL_mutex* mutex;
142 ThreadPoolItem* thread;
143
TimerDataTimerData144 TimerData() : timer(NULL), userData(NULL), interval(0), once(false), quitSignal(false), quitCond(NULL), mutex(NULL), thread(NULL) {
145 mutex = SDL_CreateMutex();
146 quitCond = SDL_CreateCond();
147 // TODO: not threadsafe
148 //timers.push_back(data); // Add it to the global timer array
149 }
~TimerDataTimerData150 ~TimerData() {
151 breakThread();
152 if(thread) threadPool->wait(thread, NULL);
153 thread = NULL;
154 SDL_DestroyMutex(mutex); mutex = NULL;
155 SDL_DestroyCond(quitCond); quitCond = NULL;
156 RemoveTimerFromGlobalList(this);
157 }
158
breakThreadTimerData159 void breakThread() {
160 quitSignal = true;
161 SDL_CondSignal(quitCond);
162 }
163
startThreadTimerData164 void startThread() {
165 assert(thread == NULL);
166
167 struct TimerHandler : Action {
168 TimerData* data;
169
170 // thread function
171 int handle() {
172
173 SDL_mutexP(data->mutex);
174 while(true) {
175 SDL_CondWaitTimeout(data->quitCond, data->mutex, data->interval);
176
177 bool lastEvent = data->once || data->quitSignal;
178 onInternTimerSignal.pushToMainQueue(InternTimerEventData(data, lastEvent));
179
180 if(lastEvent) {
181 SDL_mutexV(data->mutex);
182 // we have to return it here to ensure that this callback is never called again
183 // we also have to ensure that there is only *one* event with lastEvent=true
184 // (and this event has to be of course the last event for this timer in the queue)
185 return 0;
186 }
187 }
188
189 return -1;
190 }
191
192 TimerHandler(TimerData* d) : data(d) {}
193 };
194
195 thread = threadPool->start(new TimerHandler(this), name + " timer");
196 }
197
198 static void handleEvent(InternTimerEventData data);
199 };
200
InitTimerSystem()201 static void InitTimerSystem() {
202 if(timerSystem_inited) return;
203 onInternTimerSignal.handler() = getEventHandler(&TimerData::handleEvent);
204 TimerGlobalMutex = SDL_CreateMutex();
205 timerSystem_inited = true;
206 }
207
208 // Global list that holds info about headless timers
209 // Used to make sure there are no memory leaks
210 // TODO: not threadsafe
211 // TODO: why not std::set?
212 std::list<TimerData *> timers;
213
214 //////////////////
215 // Initialize working with timers
InitializeTimers()216 void InitializeTimers()
217 {
218
219 }
220
221 ///////////////////////
222 // Shut down and free all running timers
ShutdownTimers()223 void ShutdownTimers()
224 {
225 // TODO: not threadsafe
226 // TODO: why not std::set?
227 //assert(!EventSystemInited()); // Make sure the event system is shut down (to avoid double freed memory)
228
229 /*
230 // Stop and free all the running timers
231 for (std::list<TimerData *>::iterator it = timers.begin(); it != timers.end(); it++) {
232
233 // Call the user function, because it might free some data
234 Event<Timer::EventData>::Handler& handler = (*it)->timer ? (*it)->timer->onTimer.handler().get() : (*it)->onTimerHandler.get();
235 bool cont = true;
236 (*it)->breakThread();
237 if (&handler) // Make sure it exists
238 handler(Timer::EventData(NULL, (*it)->userData, cont));
239
240 delete (*it);
241 }
242
243 timers.clear();*/
244 }
245
246 /////////////////////////
247 // Removes the timer from the global list (called when the timer is stopped/destroyed)
RemoveTimerFromGlobalList(TimerData * data)248 static void RemoveTimerFromGlobalList(TimerData *data)
249 {
250 // TODO: not threadsafe
251 // TODO: why not std::set?
252 /*
253 for (std::list<TimerData *>::iterator it = timers.begin(); it != timers.end(); it++) {
254 if (*it == data) {
255 timers.erase(it);
256 break;
257 }
258 }*/
259 }
260
261
262 ////////////////
263 // Constructors
Timer()264 Timer::Timer() :
265 name(""), userData(NULL), interval(1000),
266 once(false), m_lastData(NULL) {
267 InitTimerSystem();
268 }
269
Timer(const std::string & nam,Null,void * dat,Uint32 t,bool o)270 Timer::Timer(const std::string& nam, Null, void* dat, Uint32 t, bool o) :
271 name(nam), userData(dat), interval(t),
272 once(o), m_lastData(NULL) {
273 InitTimerSystem();
274 }
275
Timer(const std::string & nam,void (* fct)(EventData dat),void * dat,Uint32 t,bool o)276 Timer::Timer(const std::string& nam, void (*fct)(EventData dat), void* dat, Uint32 t, bool o) :
277 name(nam), userData(dat), interval(t),
278 once(o), m_lastData(NULL) {
279 onTimer.handler() = getEventHandler(fct);
280 InitTimerSystem();
281 }
282
Timer(const std::string & nam,Ref<Event<EventData>::Handler> hndl,void * dat,Uint32 t,bool o)283 Timer::Timer(const std::string& nam, Ref<Event<EventData>::Handler> hndl, void* dat, Uint32 t, bool o) :
284 name(nam), userData(dat), interval(t),
285 once(o), m_lastData(NULL) {
286 onTimer.handler() = hndl;
287 InitTimerSystem();
288 }
289
290
291 ////////////////
292 // Destructor
~Timer()293 Timer::~Timer()
294 {
295 // TODO: because of this, headless timers will cause memleaks if still running when quitting
296 // (because they can't get any quit signal and won't be freed here). In handleEvent
297 // there should be check for tLX->bQuitGame && timer_data->timer == NULL (headless timer) and the timer should
298 // be stopped if that condition is met (it is a bit dirty solution, if you think of a better one, implement it
299 // and remove this)
300
301 // Stop the timer if running and not headless
302 stop();
303 }
304
305
306
307
308 /////////////////
309 // Returns true if this timer is running
running()310 bool Timer::running() { return m_lastData != NULL; }
311
312 ////////////////
313 // Starts the timer
start()314 bool Timer::start()
315 {
316 // Stop if running
317 stop();
318
319 SDL_mutexP(TimerGlobalMutex);
320
321 // Copy the info to timer data structure and run the timer
322 TimerData* data = new TimerData;
323 m_lastData = data;
324 //printf("%s: %p: m_lastData %p\n", __FUNCTION__, this, m_lastData);
325
326 data->timer = this;
327 data->name = name;
328 data->userData = userData;
329 data->interval = interval;
330 data->once = once;
331 data->quitSignal = false;
332
333 if(data->name == "") {
334 warnings << "unnamed timer is started" << endl;
335 data->name = "unnamed";
336 }
337
338 data->startThread();
339
340 SDL_mutexV(TimerGlobalMutex);
341
342 return true;
343 }
344
345 //////////////////
346 // Starts the timer without pointing at "this" object (it means the timer object can be a temporary local variable)
startHeadless()347 bool Timer::startHeadless()
348 {
349 SDL_mutexP(TimerGlobalMutex);
350
351 // Copy the info to timer data structure and run the timer
352 TimerData* data = new TimerData;
353 //printf("%s: %p: data %p\n", __FUNCTION__, this, data);
354 data->timer = NULL;
355 data->name = name;
356 data->onTimerHandler = onTimer.handler().get();
357 data->userData = userData;
358 data->interval = interval;
359 data->once = once;
360 data->quitSignal = false;
361
362 if(data->name == "") {
363 warnings << "unnamed timer is started headless" << endl;
364 data->name = "unnamed headless";
365 }
366
367 if(!data->once) {
368 // It's hard to debug headless timers with once=false, thus we just disable them.
369 warnings << "headless timer " << name << " has once=false, it is forced once=true now" << endl;
370 data->once = true;
371 }
372
373 data->startThread();
374
375 SDL_mutexV(TimerGlobalMutex);
376
377 return true;
378 }
379
380 ////////////////
381 // Stops the timer
stop()382 void Timer::stop()
383 {
384 SDL_mutexP(TimerGlobalMutex);
385 // Already stopped
386 if (!running()) {
387 SDL_mutexV(TimerGlobalMutex);
388 return;
389 }
390 //printf("%s: %p: m_lastData %p\n", __FUNCTION__, this, m_lastData);
391 SDL_mutex* dataMutex = m_lastData->mutex;
392 SDL_mutexP(dataMutex);
393 m_lastData->breakThread(); // it will be removed in the last event
394 m_lastData->timer = NULL;
395 m_lastData = NULL;
396 SDL_mutexV(dataMutex);
397 SDL_mutexV(TimerGlobalMutex);
398 }
399
400
401
402 ///////////////
403 // Handle the timer event, called from HandleNextEvent
handleEvent(InternTimerEventData data)404 void TimerData::handleEvent(InternTimerEventData data)
405 {
406 TimerData* timer_data = data.data;
407 if(timer_data == NULL) {
408 errors << "Timer_handleEvent: timer_data unset" << endl;
409 return;
410 }
411
412 // Run the client function (if no quitSignal) and quit the timer if it returns false
413 // Also quit if we got last event signal
414 SDL_mutexP(timer_data->mutex);
415 if( !timer_data->quitSignal ) {
416 Event<Timer::EventData>::HandlerList handlers = timer_data->timer ? timer_data->timer->onTimer.handler().get() : timer_data->onTimerHandler;
417 bool shouldContinue = true;
418 Timer::EventData eventData = Timer::EventData(timer_data->timer, timer_data->userData, shouldContinue);
419 SDL_mutexV(timer_data->mutex);
420
421 Event<Timer::EventData>::callHandlers(handlers, eventData);
422
423 SDL_mutexP(TimerGlobalMutex);
424 SDL_mutexP(timer_data->mutex);
425 if (data.lastEvent) {
426 //printf("%s: timer_data %p timer %p stop %d\n", __FUNCTION__, timer_data, timer_data->timer, timer_data->timer ? (timer_data->timer->m_lastData == timer_data) : 0);
427 if (timer_data->timer && timer_data->timer->m_lastData == timer_data) {
428 timer_data->timer->m_lastData = NULL;
429 timer_data->timer = NULL;
430 }
431 timer_data->breakThread();
432 }
433 SDL_mutexV(TimerGlobalMutex);
434 }
435 SDL_mutexV(timer_data->mutex);
436
437 if(data.lastEvent) { // last-event-signal
438 // we can delete here as we have ensured that this is realy the last event
439 //printf("%s: delete timer_data %p\n", __FUNCTION__, timer_data);
440 delete timer_data;
441 }
442 }
443
444 //
445 // StopWatch class
446 //
447
StopWatch(const std::string & name)448 StopWatch::StopWatch(const std::string& name)
449 {
450 this->name = name;
451 start = GetTime();
452 }
453
~StopWatch()454 StopWatch::~StopWatch()
455 {
456 hints << name << " took " << (GetTime() - start).milliseconds() << " ms" << endl;
457 }
458