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 				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 
    158 struct deTimer_s
    159 {
    160 	deTimerCallback		callback;
    161 	void*				callbackArg;
    162 
    163 	timer_t				timer;
    164 
    165 	deBool				isActive;
    166 };
    167 
    168 static void timerCallback (union sigval val)
    169 {
    170 	const deTimer* timer = (const deTimer*)val.sival_ptr;
    171 	timer->callback(timer->callbackArg);
    172 }
    173 
    174 deTimer* 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 
    200 void deTimer_destroy (deTimer* timer)
    201 {
    202 	DE_ASSERT(timer);
    203 
    204 	timer_delete(timer->timer);
    205 	deFree(timer);
    206 }
    207 
    208 deBool deTimer_isActive (const deTimer* timer)
    209 {
    210 	return timer->isActive;
    211 }
    212 
    213 deBool 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 
    234 deBool 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 
    255 void 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 
    281 typedef 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 
    290 typedef 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 
    302 struct deTimer_s
    303 {
    304 	deTimerCallback		callback;		/*!< Callback function.		*/
    305 	void*				callbackArg;	/*!< User pointer.			*/
    306 	deTimerThread*		curThread;		/*!< Current timer thread.	*/
    307 };
    308 
    309 static 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 
    353 static 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 
    379 deTimer* 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 
    392 void deTimer_destroy (deTimer* timer)
    393 {
    394 	if (timer->curThread)
    395 		deTimer_disable(timer);
    396 	deFree(timer);
    397 }
    398 
    399 deBool 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 
    415 deBool 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 
    426 deBool 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 
    437 void 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