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 deBool deTimer_isActive (const deTimer* timer)
75 {
76 return timer->timer != 0;
77 }
78
deTimer_scheduleSingle(deTimer * timer,int milliseconds)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
deTimer_scheduleInterval(deTimer * timer,int milliseconds)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
deTimer_disable(deTimer * timer)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
timerCallback(union sigval val)170 static void timerCallback (union sigval val)
171 {
172 const deTimer* timer = (const deTimer*)val.sival_ptr;
173 timer->callback(timer->callbackArg);
174 }
175
deTimer_create(deTimerCallback callback,void * arg)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
deTimer_destroy(deTimer * timer)202 void deTimer_destroy (deTimer* timer)
203 {
204 DE_ASSERT(timer);
205
206 timer_delete(timer->timer);
207 deFree(timer);
208 }
209
deTimer_isActive(const deTimer * timer)210 deBool deTimer_isActive (const deTimer* timer)
211 {
212 return timer->isActive;
213 }
214
deTimer_scheduleSingle(deTimer * timer,int milliseconds)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
deTimer_scheduleInterval(deTimer * timer,int milliseconds)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
deTimer_disable(deTimer * timer)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
timerThread(void * arg)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
deTimerThread_create(deTimerCallback callback,void * arg,int interval,TimerState state)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
deTimer_create(deTimerCallback callback,void * arg)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
deTimer_destroy(deTimer * timer)394 void deTimer_destroy (deTimer* timer)
395 {
396 if (timer->curThread)
397 deTimer_disable(timer);
398 deFree(timer);
399 }
400
deTimer_isActive(const deTimer * timer)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
deTimer_scheduleSingle(deTimer * timer,int milliseconds)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
deTimer_scheduleInterval(deTimer * timer,int milliseconds)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
deTimer_disable(deTimer * timer)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