Merge vk-gl-cts/vulkan-cts-1.0.2 into vk-gl-cts/master
[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                                 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 || DE_OS == DE_OS_QNX)
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
170 static void timerCallback (union sigval val)
171 {
172         const deTimer* timer = (const deTimer*)val.sival_ptr;
173         timer->callback(timer->callbackArg);
174 }
175
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
202 void deTimer_destroy (deTimer* timer)
203 {
204         DE_ASSERT(timer);
205
206         timer_delete(timer->timer);
207         deFree(timer);
208 }
209
210 deBool deTimer_isActive (const deTimer* timer)
211 {
212         return timer->isActive;
213 }
214
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
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
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
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
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
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
394 void deTimer_destroy (deTimer* timer)
395 {
396         if (timer->curThread)
397                 deTimer_disable(timer);
398         deFree(timer);
399 }
400
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
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
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
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