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