36f8190c845ff362b98eb972310740f4b58fc2c5
[platform/upstream/VK-GL-CTS.git] / framework / delibs / deutil / deTimer.c
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