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