1 /*-------------------------------------------------------------------------
2  * drawElements Utility Library
3  * ----------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Periodic timer.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "deTimer.h"
25 #include "deMemory.h"
26 #include "deThread.h"
27 
28 #if (DE_OS == DE_OS_WIN32)
29 
30 #define VC_EXTRALEAN
31 #define WIN32_LEAN_AND_MEAN
32 #include <windows.h>
33 
34 struct deTimer_s
35 {
36 	deTimerCallback		callback;
37 	void*				callbackArg;
38 
39 	HANDLE				timer;
40 };
41 
timerCallback(PVOID lpParameter,BOOLEAN timerOrWaitFired)42 static void CALLBACK timerCallback (PVOID lpParameter, BOOLEAN timerOrWaitFired)
43 {
44 	const deTimer* timer = (const deTimer*)lpParameter;
45 	DE_UNREF(timerOrWaitFired);
46 
47 	timer->callback(timer->callbackArg);
48 }
49 
deTimer_create(deTimerCallback callback,void * arg)50 deTimer* deTimer_create (deTimerCallback callback, void* arg)
51 {
52 	deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
53 
54 	if (!timer)
55 		return DE_NULL;
56 
57 	timer->callback		= callback;
58 	timer->callbackArg	= arg;
59 	timer->timer		= 0;
60 
61 	return timer;
62 }
63 
deTimer_destroy(deTimer * timer)64 void deTimer_destroy (deTimer* timer)
65 {
66 	DE_ASSERT(timer);
67 
68 	if (deTimer_isActive(timer))
69 		deTimer_disable(timer);
70 
71 	deFree(timer);
72 }
73 
deTimer_isActive(const deTimer * timer)74 deBool deTimer_isActive (const deTimer* timer)
75 {
76 	return timer->timer != 0;
77 }
78 
deTimer_scheduleSingle(deTimer * timer,int milliseconds)79 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
80 {
81 	BOOL ret;
82 
83 	DE_ASSERT(timer && milliseconds > 0);
84 
85 	if (deTimer_isActive(timer))
86 		return DE_FALSE;
87 
88 	ret = CreateTimerQueueTimer(&timer->timer, NULL, timerCallback, timer, (DWORD)milliseconds, 0, WT_EXECUTEDEFAULT);
89 
90 	if (!ret)
91 	{
92 		DE_ASSERT(!timer->timer);
93 		return DE_FALSE;
94 	}
95 
96 	return DE_TRUE;
97 }
98 
deTimer_scheduleInterval(deTimer * timer,int milliseconds)99 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
100 {
101 	BOOL ret;
102 
103 	DE_ASSERT(timer && milliseconds > 0);
104 
105 	if (deTimer_isActive(timer))
106 		return DE_FALSE;
107 
108 	ret = CreateTimerQueueTimer(&timer->timer, NULL, timerCallback, timer, (DWORD)milliseconds, (DWORD)milliseconds, WT_EXECUTEDEFAULT);
109 
110 	if (!ret)
111 	{
112 		DE_ASSERT(!timer->timer);
113 		return DE_FALSE;
114 	}
115 
116 	return DE_TRUE;
117 }
118 
deTimer_disable(deTimer * timer)119 void deTimer_disable (deTimer* timer)
120 {
121 	if (timer->timer)
122 	{
123 		const int	maxTries	= 100;
124 		HANDLE		waitEvent	= CreateEvent(NULL, FALSE, FALSE, NULL);
125 		int			tryNdx		= 0;
126 		DE_ASSERT(waitEvent);
127 
128 		for (tryNdx = 0; tryNdx < maxTries; tryNdx++)
129 		{
130 			BOOL success = DeleteTimerQueueTimer(NULL, timer->timer, waitEvent);
131 			if (success)
132 			{
133 				/* Wait for all callbacks to complete. */
134 				DWORD res = WaitForSingleObject(waitEvent, INFINITE);
135 				DE_ASSERT(res == WAIT_OBJECT_0);
136 				DE_UNREF(res);
137 				break;
138 			}
139 			else
140 			{
141 				DWORD err = GetLastError();
142 				if (err == ERROR_IO_PENDING)
143 					break; /* \todo [2013-03-21 pyry] Does this mean that callback is still in progress? */
144 				deYield();
145 			}
146 		}
147 
148 		DE_ASSERT(tryNdx < maxTries);
149 
150 		CloseHandle(waitEvent);
151 		timer->timer = 0;
152 	}
153 }
154 
155 #elif (DE_OS == DE_OS_UNIX || DE_OS == DE_OS_ANDROID || DE_OS == DE_OS_SYMBIAN || DE_OS == DE_OS_QNX)
156 
157 #include <signal.h>
158 #include <time.h>
159 
160 struct deTimer_s
161 {
162 	deTimerCallback		callback;
163 	void*				callbackArg;
164 
165 	timer_t				timer;
166 
167 	deBool				isActive;
168 };
169 
timerCallback(union sigval val)170 static void timerCallback (union sigval val)
171 {
172 	const deTimer* timer = (const deTimer*)val.sival_ptr;
173 	timer->callback(timer->callbackArg);
174 }
175 
deTimer_create(deTimerCallback callback,void * arg)176 deTimer* deTimer_create (deTimerCallback callback, void* arg)
177 {
178 	deTimer*		timer = (deTimer*)deCalloc(sizeof(deTimer));
179 	struct sigevent	sevp;
180 
181 	if (!timer)
182 		return DE_NULL;
183 
184 	deMemset(&sevp, 0, sizeof(sevp));
185 	sevp.sigev_notify			= SIGEV_THREAD;
186 	sevp.sigev_value.sival_ptr	= timer;
187 	sevp.sigev_notify_function	= timerCallback;
188 
189 	if (timer_create(CLOCK_REALTIME, &sevp, &timer->timer) != 0)
190 	{
191 		deFree(timer);
192 		return DE_NULL;
193 	}
194 
195 	timer->callback		= callback;
196 	timer->callbackArg	= arg;
197 	timer->isActive		= DE_FALSE;
198 
199 	return timer;
200 }
201 
deTimer_destroy(deTimer * timer)202 void deTimer_destroy (deTimer* timer)
203 {
204 	DE_ASSERT(timer);
205 
206 	timer_delete(timer->timer);
207 	deFree(timer);
208 }
209 
deTimer_isActive(const deTimer * timer)210 deBool deTimer_isActive (const deTimer* timer)
211 {
212 	return timer->isActive;
213 }
214 
deTimer_scheduleSingle(deTimer * timer,int milliseconds)215 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
216 {
217 	struct itimerspec tspec;
218 
219 	DE_ASSERT(timer && milliseconds > 0);
220 
221 	if (timer->isActive)
222 		return DE_FALSE;
223 
224 	tspec.it_value.tv_sec		= milliseconds / 1000;
225 	tspec.it_value.tv_nsec		= (milliseconds % 1000) * 1000;
226 	tspec.it_interval.tv_sec	= 0;
227 	tspec.it_interval.tv_nsec	= 0;
228 
229 	if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
230 		return DE_FALSE;
231 
232 	timer->isActive = DE_TRUE;
233 	return DE_TRUE;
234 }
235 
deTimer_scheduleInterval(deTimer * timer,int milliseconds)236 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
237 {
238 	struct itimerspec tspec;
239 
240 	DE_ASSERT(timer && milliseconds > 0);
241 
242 	if (timer->isActive)
243 		return DE_FALSE;
244 
245 	tspec.it_value.tv_sec		= milliseconds / 1000;
246 	tspec.it_value.tv_nsec		= (milliseconds % 1000) * 1000;
247 	tspec.it_interval.tv_sec	= tspec.it_value.tv_sec;
248 	tspec.it_interval.tv_nsec	= tspec.it_value.tv_nsec;
249 
250 	if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
251 		return DE_FALSE;
252 
253 	timer->isActive = DE_TRUE;
254 	return DE_TRUE;
255 }
256 
deTimer_disable(deTimer * timer)257 void deTimer_disable (deTimer* timer)
258 {
259 	struct itimerspec tspec;
260 
261 	DE_ASSERT(timer);
262 
263 	tspec.it_value.tv_sec		= 0;
264 	tspec.it_value.tv_nsec		= 0;
265 	tspec.it_interval.tv_sec	= 0;
266 	tspec.it_interval.tv_nsec	= 0;
267 
268 	timer_settime(timer->timer, 0, &tspec, DE_NULL);
269 
270 	/* \todo [2012-07-10 pyry] How to wait until all pending callbacks have finished? */
271 
272 	timer->isActive = DE_FALSE;
273 }
274 
275 #else
276 
277 /* Generic thread-based implementation for OSes that lack proper timers. */
278 
279 #include "deThread.h"
280 #include "deMutex.h"
281 #include "deClock.h"
282 
283 typedef enum TimerState_e
284 {
285 	TIMERSTATE_INTERVAL = 0,	/*!< Active interval timer.		*/
286 	TIMERSTATE_SINGLE,			/*!< Single callback timer.		*/
287 	TIMERSTATE_DISABLED,		/*!< Disabled timer.			*/
288 
289 	TIMERSTATE_LAST
290 } TimerState;
291 
292 typedef struct deTimerThread_s
293 {
294 	deTimerCallback		callback;		/*!< Callback function.		*/
295 	void*				callbackArg;	/*!< User pointer.			*/
296 
297 	deThread			thread;			/*!< Thread.				*/
298 	int					interval;		/*!< Timer interval.		*/
299 
300 	deMutex				lock;			/*!< State lock.			*/
301 	volatile TimerState	state;			/*!< Timer state.			*/
302 } deTimerThread;
303 
304 struct deTimer_s
305 {
306 	deTimerCallback		callback;		/*!< Callback function.		*/
307 	void*				callbackArg;	/*!< User pointer.			*/
308 	deTimerThread*		curThread;		/*!< Current timer thread.	*/
309 };
310 
timerThread(void * arg)311 static void timerThread (void* arg)
312 {
313 	deTimerThread*	thread			= (deTimerThread*)arg;
314 	int				numCallbacks	= 0;
315 	deBool			destroy			= DE_TRUE;
316 	deInt64			lastCallback	= (deInt64)deGetMicroseconds();
317 
318 	for (;;)
319 	{
320 		int sleepTime = 0;
321 
322 		deMutex_lock(thread->lock);
323 
324 		if (thread->state == TIMERSTATE_SINGLE && numCallbacks > 0)
325 		{
326 			destroy = DE_FALSE; /* Will be destroyed by deTimer_disable(). */
327 			thread->state = TIMERSTATE_DISABLED;
328 			break;
329 		}
330 		else if (thread->state == TIMERSTATE_DISABLED)
331 			break;
332 
333 		deMutex_unlock(thread->lock);
334 
335 		sleepTime = thread->interval - (int)(((deInt64)deGetMicroseconds()-lastCallback)/1000);
336 		if (sleepTime > 0)
337 			deSleep(sleepTime);
338 
339 		lastCallback = (deInt64)deGetMicroseconds();
340 		thread->callback(thread->callbackArg);
341 		numCallbacks += 1;
342 	}
343 
344 	/* State lock is held when loop is exited. */
345 	deMutex_unlock(thread->lock);
346 
347 	if (destroy)
348 	{
349 		/* Destroy thread except thread->thread. */
350 		deMutex_destroy(thread->lock);
351 		deFree(thread);
352 	}
353 }
354 
deTimerThread_create(deTimerCallback callback,void * arg,int interval,TimerState state)355 static deTimerThread* deTimerThread_create (deTimerCallback callback, void* arg, int interval, TimerState state)
356 {
357 	deTimerThread* thread = (deTimerThread*)deCalloc(sizeof(deTimerThread));
358 
359 	DE_ASSERT(state == TIMERSTATE_INTERVAL || state == TIMERSTATE_SINGLE);
360 
361 	if (!thread)
362 		return DE_NULL;
363 
364 	thread->callback	= callback;
365 	thread->callbackArg	= arg;
366 	thread->interval	= interval;
367 	thread->lock		= deMutex_create(DE_NULL);
368 	thread->state		= state;
369 
370 	thread->thread		= deThread_create(timerThread, thread, DE_NULL);
371 	if (!thread->thread)
372 	{
373 		deMutex_destroy(thread->lock);
374 		deFree(thread);
375 		return DE_NULL;
376 	}
377 
378 	return thread;
379 }
380 
deTimer_create(deTimerCallback callback,void * arg)381 deTimer* deTimer_create (deTimerCallback callback, void* arg)
382 {
383 	deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
384 
385 	if (!timer)
386 		return DE_NULL;
387 
388 	timer->callback		= callback;
389 	timer->callbackArg	= arg;
390 
391 	return timer;
392 }
393 
deTimer_destroy(deTimer * timer)394 void deTimer_destroy (deTimer* timer)
395 {
396 	if (timer->curThread)
397 		deTimer_disable(timer);
398 	deFree(timer);
399 }
400 
deTimer_isActive(const deTimer * timer)401 deBool deTimer_isActive (const deTimer* timer)
402 {
403 	if (timer->curThread)
404 	{
405 		deBool isActive = DE_FALSE;
406 
407 		deMutex_lock(timer->curThread->lock);
408 		isActive = timer->curThread->state != TIMERSTATE_LAST;
409 		deMutex_unlock(timer->curThread->lock);
410 
411 		return isActive;
412 	}
413 	else
414 		return DE_FALSE;
415 }
416 
deTimer_scheduleSingle(deTimer * timer,int milliseconds)417 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
418 {
419 	if (timer->curThread)
420 		deTimer_disable(timer);
421 
422 	DE_ASSERT(!timer->curThread);
423 	timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_SINGLE);
424 
425 	return timer->curThread != DE_NULL;
426 }
427 
deTimer_scheduleInterval(deTimer * timer,int milliseconds)428 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
429 {
430 	if (timer->curThread)
431 		deTimer_disable(timer);
432 
433 	DE_ASSERT(!timer->curThread);
434 	timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_INTERVAL);
435 
436 	return timer->curThread != DE_NULL;
437 }
438 
deTimer_disable(deTimer * timer)439 void deTimer_disable (deTimer* timer)
440 {
441 	if (!timer->curThread)
442 		return;
443 
444 	deMutex_lock(timer->curThread->lock);
445 
446 	if (timer->curThread->state != TIMERSTATE_DISABLED)
447 	{
448 		/* Just set state to disabled and destroy thread handle. */
449 		/* \note Assumes that deThread_destroy() can be called while thread is still running
450 		 *       and it will not terminate the thread.
451 		 */
452 		timer->curThread->state = TIMERSTATE_DISABLED;
453 		deThread_destroy(timer->curThread->thread);
454 		timer->curThread->thread = 0;
455 		deMutex_unlock(timer->curThread->lock);
456 
457 		/* Thread will destroy timer->curThread. */
458 	}
459 	else
460 	{
461 		/* Single timer has expired - we must destroy whole thread structure. */
462 		deMutex_unlock(timer->curThread->lock);
463 		deThread_destroy(timer->curThread->thread);
464 		deMutex_destroy(timer->curThread->lock);
465 		deFree(timer->curThread);
466 	}
467 
468 	timer->curThread = DE_NULL;
469 }
470 
471 #endif
472