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
34struct deTimer_s
35{
36	deTimerCallback		callback;
37	void*				callbackArg;
38
39	HANDLE				timer;
40};
41
42static 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
50deTimer* 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
64void 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
74deBool deTimer_isActive (const deTimer* timer)
75{
76	return timer->timer != 0;
77}
78
79deBool 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
99deBool 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
119void 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				DE_VERIFY(WaitForSingleObject(waitEvent, INFINITE) == WAIT_OBJECT_0);
135				break;
136			}
137			else
138			{
139				DWORD err = GetLastError();
140				if (err == ERROR_IO_PENDING)
141					break; /* \todo [2013-03-21 pyry] Does this mean that callback is still in progress? */
142				deYield();
143			}
144		}
145
146		DE_ASSERT(tryNdx < maxTries);
147
148		CloseHandle(waitEvent);
149		timer->timer = 0;
150	}
151}
152
153#elif (DE_OS == DE_OS_UNIX || DE_OS == DE_OS_ANDROID || DE_OS == DE_OS_SYMBIAN)
154
155#include <signal.h>
156#include <time.h>
157
158struct deTimer_s
159{
160	deTimerCallback		callback;
161	void*				callbackArg;
162
163	timer_t				timer;
164
165	deBool				isActive;
166};
167
168static void timerCallback (union sigval val)
169{
170	const deTimer* timer = (const deTimer*)val.sival_ptr;
171	timer->callback(timer->callbackArg);
172}
173
174deTimer* deTimer_create (deTimerCallback callback, void* arg)
175{
176	deTimer*		timer = (deTimer*)deCalloc(sizeof(deTimer));
177	struct sigevent	sevp;
178
179	if (!timer)
180		return DE_NULL;
181
182	deMemset(&sevp, 0, sizeof(sevp));
183	sevp.sigev_notify			= SIGEV_THREAD;
184	sevp.sigev_value.sival_ptr	= timer;
185	sevp.sigev_notify_function	= timerCallback;
186
187	if (timer_create(CLOCK_REALTIME, &sevp, &timer->timer) != 0)
188	{
189		deFree(timer);
190		return DE_NULL;
191	}
192
193	timer->callback		= callback;
194	timer->callbackArg	= arg;
195	timer->isActive		= DE_FALSE;
196
197	return timer;
198}
199
200void deTimer_destroy (deTimer* timer)
201{
202	DE_ASSERT(timer);
203
204	timer_delete(timer->timer);
205	deFree(timer);
206}
207
208deBool deTimer_isActive (const deTimer* timer)
209{
210	return timer->isActive;
211}
212
213deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
214{
215	struct itimerspec tspec;
216
217	DE_ASSERT(timer && milliseconds > 0);
218
219	if (timer->isActive)
220		return DE_FALSE;
221
222	tspec.it_value.tv_sec		= milliseconds / 1000;
223	tspec.it_value.tv_nsec		= (milliseconds % 1000) * 1000;
224	tspec.it_interval.tv_sec	= 0;
225	tspec.it_interval.tv_nsec	= 0;
226
227	if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
228		return DE_FALSE;
229
230	timer->isActive = DE_TRUE;
231	return DE_TRUE;
232}
233
234deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
235{
236	struct itimerspec tspec;
237
238	DE_ASSERT(timer && milliseconds > 0);
239
240	if (timer->isActive)
241		return DE_FALSE;
242
243	tspec.it_value.tv_sec		= milliseconds / 1000;
244	tspec.it_value.tv_nsec		= (milliseconds % 1000) * 1000;
245	tspec.it_interval.tv_sec	= tspec.it_value.tv_sec;
246	tspec.it_interval.tv_nsec	= tspec.it_value.tv_nsec;
247
248	if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
249		return DE_FALSE;
250
251	timer->isActive = DE_TRUE;
252	return DE_TRUE;
253}
254
255void deTimer_disable (deTimer* timer)
256{
257	struct itimerspec tspec;
258
259	DE_ASSERT(timer);
260
261	tspec.it_value.tv_sec		= 0;
262	tspec.it_value.tv_nsec		= 0;
263	tspec.it_interval.tv_sec	= 0;
264	tspec.it_interval.tv_nsec	= 0;
265
266	timer_settime(timer->timer, 0, &tspec, DE_NULL);
267
268	/* \todo [2012-07-10 pyry] How to wait until all pending callbacks have finished? */
269
270	timer->isActive = DE_FALSE;
271}
272
273#else
274
275/* Generic thread-based implementation for OSes that lack proper timers. */
276
277#include "deThread.h"
278#include "deMutex.h"
279#include "deClock.h"
280
281typedef enum TimerState_e
282{
283	TIMERSTATE_INTERVAL = 0,	/*!< Active interval timer.		*/
284	TIMERSTATE_SINGLE,			/*!< Single callback timer.		*/
285	TIMERSTATE_DISABLED,		/*!< Disabled timer.			*/
286
287	TIMERSTATE_LAST
288} TimerState;
289
290typedef struct deTimerThread_s
291{
292	deTimerCallback		callback;		/*!< Callback function.		*/
293	void*				callbackArg;	/*!< User pointer.			*/
294
295	deThread			thread;			/*!< Thread.				*/
296	int					interval;		/*!< Timer interval.		*/
297
298	deMutex				lock;			/*!< State lock.			*/
299	volatile TimerState	state;			/*!< Timer state.			*/
300} deTimerThread;
301
302struct deTimer_s
303{
304	deTimerCallback		callback;		/*!< Callback function.		*/
305	void*				callbackArg;	/*!< User pointer.			*/
306	deTimerThread*		curThread;		/*!< Current timer thread.	*/
307};
308
309static void timerThread (void* arg)
310{
311	deTimerThread*	thread			= (deTimerThread*)arg;
312	int				numCallbacks	= 0;
313	deBool			destroy			= DE_TRUE;
314	deInt64			lastCallback	= (deInt64)deGetMicroseconds();
315
316	for (;;)
317	{
318		int sleepTime = 0;
319
320		deMutex_lock(thread->lock);
321
322		if (thread->state == TIMERSTATE_SINGLE && numCallbacks > 0)
323		{
324			destroy = DE_FALSE; /* Will be destroyed by deTimer_disable(). */
325			thread->state = TIMERSTATE_DISABLED;
326			break;
327		}
328		else if (thread->state == TIMERSTATE_DISABLED)
329			break;
330
331		deMutex_unlock(thread->lock);
332
333		sleepTime = thread->interval - (int)(((deInt64)deGetMicroseconds()-lastCallback)/1000);
334		if (sleepTime > 0)
335			deSleep(sleepTime);
336
337		lastCallback = (deInt64)deGetMicroseconds();
338		thread->callback(thread->callbackArg);
339		numCallbacks += 1;
340	}
341
342	/* State lock is held when loop is exited. */
343	deMutex_unlock(thread->lock);
344
345	if (destroy)
346	{
347		/* Destroy thread except thread->thread. */
348		deMutex_destroy(thread->lock);
349		deFree(thread);
350	}
351}
352
353static deTimerThread* deTimerThread_create (deTimerCallback callback, void* arg, int interval, TimerState state)
354{
355	deTimerThread* thread = (deTimerThread*)deCalloc(sizeof(deTimerThread));
356
357	DE_ASSERT(state == TIMERSTATE_INTERVAL || state == TIMERSTATE_SINGLE);
358
359	if (!thread)
360		return DE_NULL;
361
362	thread->callback	= callback;
363	thread->callbackArg	= arg;
364	thread->interval	= interval;
365	thread->lock		= deMutex_create(DE_NULL);
366	thread->state		= state;
367
368	thread->thread		= deThread_create(timerThread, thread, DE_NULL);
369	if (!thread->thread)
370	{
371		deMutex_destroy(thread->lock);
372		deFree(thread);
373		return DE_NULL;
374	}
375
376	return thread;
377}
378
379deTimer* deTimer_create (deTimerCallback callback, void* arg)
380{
381	deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
382
383	if (!timer)
384		return DE_NULL;
385
386	timer->callback		= callback;
387	timer->callbackArg	= arg;
388
389	return timer;
390}
391
392void deTimer_destroy (deTimer* timer)
393{
394	if (timer->curThread)
395		deTimer_disable(timer);
396	deFree(timer);
397}
398
399deBool deTimer_isActive (const deTimer* timer)
400{
401	if (timer->curThread)
402	{
403		deBool isActive = DE_FALSE;
404
405		deMutex_lock(timer->curThread->lock);
406		isActive = timer->curThread->state != TIMERSTATE_LAST;
407		deMutex_unlock(timer->curThread->lock);
408
409		return isActive;
410	}
411	else
412		return DE_FALSE;
413}
414
415deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
416{
417	if (timer->curThread)
418		deTimer_disable(timer);
419
420	DE_ASSERT(!timer->curThread);
421	timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_SINGLE);
422
423	return timer->curThread != DE_NULL;
424}
425
426deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
427{
428	if (timer->curThread)
429		deTimer_disable(timer);
430
431	DE_ASSERT(!timer->curThread);
432	timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_INTERVAL);
433
434	return timer->curThread != DE_NULL;
435}
436
437void deTimer_disable (deTimer* timer)
438{
439	if (!timer->curThread)
440		return;
441
442	deMutex_lock(timer->curThread->lock);
443
444	if (timer->curThread->state != TIMERSTATE_DISABLED)
445	{
446		/* Just set state to disabled and destroy thread handle. */
447		/* \note Assumes that deThread_destroy() can be called while thread is still running
448		 *       and it will not terminate the thread.
449		 */
450		timer->curThread->state = TIMERSTATE_DISABLED;
451		deThread_destroy(timer->curThread->thread);
452		timer->curThread->thread = 0;
453		deMutex_unlock(timer->curThread->lock);
454
455		/* Thread will destroy timer->curThread. */
456	}
457	else
458	{
459		/* Single timer has expired - we must destroy whole thread structure. */
460		deMutex_unlock(timer->curThread->lock);
461		deThread_destroy(timer->curThread->thread);
462		deMutex_destroy(timer->curThread->lock);
463		deFree(timer->curThread);
464	}
465
466	timer->curThread = DE_NULL;
467}
468
469#endif
470