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 DWORD res = WaitForSingleObject(waitEvent, INFINITE);
135 DE_ASSERT(res == WAIT_OBJECT_0);
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? */
148 DE_ASSERT(tryNdx < maxTries);
150 CloseHandle(waitEvent);
155 #elif (DE_OS == DE_OS_UNIX || DE_OS == DE_OS_ANDROID || DE_OS == DE_OS_SYMBIAN)
162 deTimerCallback callback;
170 static void timerCallback (union sigval val)
172 const deTimer* timer = (const deTimer*)val.sival_ptr;
173 timer->callback(timer->callbackArg);
176 deTimer* deTimer_create (deTimerCallback callback, void* arg)
178 deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
179 struct sigevent sevp;
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;
189 if (timer_create(CLOCK_REALTIME, &sevp, &timer->timer) != 0)
195 timer->callback = callback;
196 timer->callbackArg = arg;
197 timer->isActive = DE_FALSE;
202 void deTimer_destroy (deTimer* timer)
206 timer_delete(timer->timer);
210 deBool deTimer_isActive (const deTimer* timer)
212 return timer->isActive;
215 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
217 struct itimerspec tspec;
219 DE_ASSERT(timer && milliseconds > 0);
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;
229 if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
232 timer->isActive = DE_TRUE;
236 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
238 struct itimerspec tspec;
240 DE_ASSERT(timer && milliseconds > 0);
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;
250 if (timer_settime(timer->timer, 0, &tspec, DE_NULL) != 0)
253 timer->isActive = DE_TRUE;
257 void deTimer_disable (deTimer* timer)
259 struct itimerspec tspec;
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;
268 timer_settime(timer->timer, 0, &tspec, DE_NULL);
270 /* \todo [2012-07-10 pyry] How to wait until all pending callbacks have finished? */
272 timer->isActive = DE_FALSE;
277 /* Generic thread-based implementation for OSes that lack proper timers. */
279 #include "deThread.h"
283 typedef enum TimerState_e
285 TIMERSTATE_INTERVAL = 0, /*!< Active interval timer. */
286 TIMERSTATE_SINGLE, /*!< Single callback timer. */
287 TIMERSTATE_DISABLED, /*!< Disabled timer. */
292 typedef struct deTimerThread_s
294 deTimerCallback callback; /*!< Callback function. */
295 void* callbackArg; /*!< User pointer. */
297 deThread thread; /*!< Thread. */
298 int interval; /*!< Timer interval. */
300 deMutex lock; /*!< State lock. */
301 volatile TimerState state; /*!< Timer state. */
306 deTimerCallback callback; /*!< Callback function. */
307 void* callbackArg; /*!< User pointer. */
308 deTimerThread* curThread; /*!< Current timer thread. */
311 static void timerThread (void* arg)
313 deTimerThread* thread = (deTimerThread*)arg;
314 int numCallbacks = 0;
315 deBool destroy = DE_TRUE;
316 deInt64 lastCallback = (deInt64)deGetMicroseconds();
322 deMutex_lock(thread->lock);
324 if (thread->state == TIMERSTATE_SINGLE && numCallbacks > 0)
326 destroy = DE_FALSE; /* Will be destroyed by deTimer_disable(). */
327 thread->state = TIMERSTATE_DISABLED;
330 else if (thread->state == TIMERSTATE_DISABLED)
333 deMutex_unlock(thread->lock);
335 sleepTime = thread->interval - (int)(((deInt64)deGetMicroseconds()-lastCallback)/1000);
339 lastCallback = (deInt64)deGetMicroseconds();
340 thread->callback(thread->callbackArg);
344 /* State lock is held when loop is exited. */
345 deMutex_unlock(thread->lock);
349 /* Destroy thread except thread->thread. */
350 deMutex_destroy(thread->lock);
355 static deTimerThread* deTimerThread_create (deTimerCallback callback, void* arg, int interval, TimerState state)
357 deTimerThread* thread = (deTimerThread*)deCalloc(sizeof(deTimerThread));
359 DE_ASSERT(state == TIMERSTATE_INTERVAL || state == TIMERSTATE_SINGLE);
364 thread->callback = callback;
365 thread->callbackArg = arg;
366 thread->interval = interval;
367 thread->lock = deMutex_create(DE_NULL);
368 thread->state = state;
370 thread->thread = deThread_create(timerThread, thread, DE_NULL);
373 deMutex_destroy(thread->lock);
381 deTimer* deTimer_create (deTimerCallback callback, void* arg)
383 deTimer* timer = (deTimer*)deCalloc(sizeof(deTimer));
388 timer->callback = callback;
389 timer->callbackArg = arg;
394 void deTimer_destroy (deTimer* timer)
396 if (timer->curThread)
397 deTimer_disable(timer);
401 deBool deTimer_isActive (const deTimer* timer)
403 if (timer->curThread)
405 deBool isActive = DE_FALSE;
407 deMutex_lock(timer->curThread->lock);
408 isActive = timer->curThread->state != TIMERSTATE_LAST;
409 deMutex_unlock(timer->curThread->lock);
417 deBool deTimer_scheduleSingle (deTimer* timer, int milliseconds)
419 if (timer->curThread)
420 deTimer_disable(timer);
422 DE_ASSERT(!timer->curThread);
423 timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_SINGLE);
425 return timer->curThread != DE_NULL;
428 deBool deTimer_scheduleInterval (deTimer* timer, int milliseconds)
430 if (timer->curThread)
431 deTimer_disable(timer);
433 DE_ASSERT(!timer->curThread);
434 timer->curThread = deTimerThread_create(timer->callback, timer->callbackArg, milliseconds, TIMERSTATE_INTERVAL);
436 return timer->curThread != DE_NULL;
439 void deTimer_disable (deTimer* timer)
441 if (!timer->curThread)
444 deMutex_lock(timer->curThread->lock);
446 if (timer->curThread->state != TIMERSTATE_DISABLED)
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.
452 timer->curThread->state = TIMERSTATE_DISABLED;
453 deThread_destroy(timer->curThread->thread);
454 timer->curThread->thread = 0;
455 deMutex_unlock(timer->curThread->lock);
457 /* Thread will destroy timer->curThread. */
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);
468 timer->curThread = DE_NULL;