Home | History | Annotate | Download | only in deutil
      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 
     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 
     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 
     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 
     74 deBool deTimer_isActive (const deTimer* timer)
     75 {
     76 	return timer->timer != 0;
     77 }
     78 
     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 
     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 
    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)
    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 
    170 static void timerCallback (union sigval val)
    171 {
    172 	const deTimer* timer = (const deTimer*)val.sival_ptr;
    173 	timer->callback(timer->callbackArg);
    174 }
    175 
    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 
    202 void deTimer_destroy (deTimer* timer)
    203 {
    204 	DE_ASSERT(timer);
    205 
    206 	timer_delete(timer->timer);
    207 	deFree(timer);
    208 }
    209 
    210 deBool deTimer_isActive (const deTimer* timer)
    211 {
    212 	return timer->isActive;
    213 }
    214 
    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 
    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 
    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 
    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 
    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 
    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 
    394 void deTimer_destroy (deTimer* timer)
    395 {
    396 	if (timer->curThread)
    397 		deTimer_disable(timer);
    398 	deFree(timer);
    399 }
    400 
    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 
    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 
    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 
    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