1 /*-------------------------------------------------------------------------
2 * drawElements Utility Library
3 * ----------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Periodic timer.
22 *//*--------------------------------------------------------------------*/
28 #if (DE_OS == DE_OS_WIN32)
31 #define WIN32_LEAN_AND_MEAN
36 deTimerCallback callback;
42 static void CALLBACK timerCallback (PVOID lpParameter, BOOLEAN timerOrWaitFired)
44 const deTimer* timer = (const deTimer*)lpParameter;
45 DE_UNREF(timerOrWaitFired);
47 timer->callback(timer->callbackArg);
50 deTimer* deTimer_create (deTimerCallback callback, void* arg)
52 deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
57 timer->callback = callback;
58 timer->callbackArg = arg;
64 void deTimer_destroy (deTimer* timer)
68 if (deTimer_isActive(timer))
69 deTimer_disable(timer);
74 deBool deTimer_isActive (const deTimer* timer)
76 return timer->timer != 0;
79 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
83 DE_ASSERT(timer && milliseconds > 0);
85 if (deTimer_isActive(timer))
88 ret = CreateTimerQueueTimer(&timer->timer, NULL, timerCallback, timer, (DWORD)milliseconds, 0, WT_EXECUTEDEFAULT);
92 DE_ASSERT(!timer->timer);
99 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
103 DE_ASSERT(timer && milliseconds > 0);
105 if (deTimer_isActive(timer))
108 ret = CreateTimerQueueTimer(&timer->timer, NULL, timerCallback, timer, (DWORD)milliseconds, (DWORD)milliseconds, WT_EXECUTEDEFAULT);
112 DE_ASSERT(!timer->timer);
119 void deTimer_disable (deTimer* timer)
123 const int maxTries = 100;
124 HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
126 DE_ASSERT(waitEvent);
128 for (tryNdx = 0; tryNdx < maxTries; tryNdx++)
130 BOOL success = DeleteTimerQueueTimer(NULL, timer->timer, waitEvent);
133 /* Wait for all callbacks to complete. */
134 DE_VERIFY(WaitForSingleObject(waitEvent, INFINITE) == WAIT_OBJECT_0);
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? */
146 DE_ASSERT(tryNdx < maxTries);
148 CloseHandle(waitEvent);
153 #elif (DE_OS == DE_OS_UNIX || DE_OS == DE_OS_ANDROID || DE_OS == DE_OS_SYMBIAN)
160 deTimerCallback callback;
168 static void timerCallback (union sigval val)
170 const deTimer* timer = (const deTimer*)val.sival_ptr;
171 timer->callback(timer->callbackArg);
174 deTimer* deTimer_create (deTimerCallback callback, void* arg)
176 deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
177 struct sigevent sevp;
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;
187 if (timer_create(CLOCK_REALTIME, &sevp, &timer->timer) != 0)
193 timer->callback = callback;
194 timer->callbackArg = arg;
195 timer->isActive = DE_FALSE;
200 void deTimer_destroy (deTimer* timer)
204 timer_delete(timer->timer);
208 deBool deTimer_isActive (const deTimer* timer)
210 return timer->isActive;
213 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
215 struct itimerspec tspec;
217 DE_ASSERT(timer && milliseconds > 0);
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;
227 if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
230 timer->isActive = DE_TRUE;
234 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
236 struct itimerspec tspec;
238 DE_ASSERT(timer && milliseconds > 0);
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;
248 if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
251 timer->isActive = DE_TRUE;
255 void deTimer_disable (deTimer* timer)
257 struct itimerspec tspec;
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;
266 timer_settime(timer->timer, 0, &tspec, DE_NULL);
268 /* \todo [2012-07-10 pyry] How to wait until all pending callbacks have finished? */
270 timer->isActive = DE_FALSE;
275 /* Generic thread-based implementation for OSes that lack proper timers. */
277 #include "deThread.h"
281 typedef enum TimerState_e
283 TIMERSTATE_INTERVAL = 0, /*!< Active interval timer. */
284 TIMERSTATE_SINGLE, /*!< Single callback timer. */
285 TIMERSTATE_DISABLED, /*!< Disabled timer. */
290 typedef struct deTimerThread_s
292 deTimerCallback callback; /*!< Callback function. */
293 void* callbackArg; /*!< User pointer. */
295 deThread thread; /*!< Thread. */
296 int interval; /*!< Timer interval. */
298 deMutex lock; /*!< State lock. */
299 volatile TimerState state; /*!< Timer state. */
304 deTimerCallback callback; /*!< Callback function. */
305 void* callbackArg; /*!< User pointer. */
306 deTimerThread* curThread; /*!< Current timer thread. */
309 static void timerThread (void* arg)
311 deTimerThread* thread = (deTimerThread*)arg;
312 int numCallbacks = 0;
313 deBool destroy = DE_TRUE;
314 deInt64 lastCallback = (deInt64)deGetMicroseconds();
320 deMutex_lock(thread->lock);
322 if (thread->state == TIMERSTATE_SINGLE && numCallbacks > 0)
324 destroy = DE_FALSE; /* Will be destroyed by deTimer_disable(). */
325 thread->state = TIMERSTATE_DISABLED;
328 else if (thread->state == TIMERSTATE_DISABLED)
331 deMutex_unlock(thread->lock);
333 sleepTime = thread->interval - (int)(((deInt64)deGetMicroseconds()-lastCallback)/1000);
337 lastCallback = (deInt64)deGetMicroseconds();
338 thread->callback(thread->callbackArg);
342 /* State lock is held when loop is exited. */
343 deMutex_unlock(thread->lock);
347 /* Destroy thread except thread->thread. */
348 deMutex_destroy(thread->lock);
353 static deTimerThread* deTimerThread_create (deTimerCallback callback, void* arg, int interval, TimerState state)
355 deTimerThread* thread = (deTimerThread*)deCalloc(sizeof(deTimerThread));
357 DE_ASSERT(state == TIMERSTATE_INTERVAL || state == TIMERSTATE_SINGLE);
362 thread->callback = callback;
363 thread->callbackArg = arg;
364 thread->interval = interval;
365 thread->lock = deMutex_create(DE_NULL);
366 thread->state = state;
368 thread->thread = deThread_create(timerThread, thread, DE_NULL);
371 deMutex_destroy(thread->lock);
379 deTimer* deTimer_create (deTimerCallback callback, void* arg)
381 deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
386 timer->callback = callback;
387 timer->callbackArg = arg;
392 void deTimer_destroy (deTimer* timer)
394 if (timer->curThread)
395 deTimer_disable(timer);
399 deBool deTimer_isActive (const deTimer* timer)
401 if (timer->curThread)
403 deBool isActive = DE_FALSE;
405 deMutex_lock(timer->curThread->lock);
406 isActive = timer->curThread->state != TIMERSTATE_LAST;
407 deMutex_unlock(timer->curThread->lock);
415 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
417 if (timer->curThread)
418 deTimer_disable(timer);
420 DE_ASSERT(!timer->curThread);
421 timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_SINGLE);
423 return timer->curThread != DE_NULL;
426 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
428 if (timer->curThread)
429 deTimer_disable(timer);
431 DE_ASSERT(!timer->curThread);
432 timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_INTERVAL);
434 return timer->curThread != DE_NULL;
437 void deTimer_disable (deTimer* timer)
439 if (!timer->curThread)
442 deMutex_lock(timer->curThread->lock);
444 if (timer->curThread->state != TIMERSTATE_DISABLED)
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.
450 timer->curThread->state = TIMERSTATE_DISABLED;
451 deThread_destroy(timer->curThread->thread);
452 timer->curThread->thread = 0;
453 deMutex_unlock(timer->curThread->lock);
455 /* Thread will destroy timer->curThread. */
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);
466 timer->curThread = DE_NULL;