xref: /aosp_15_r20/external/deqp/framework/delibs/deutil/deTimer.c (revision 35238bce31c2a825756842865a792f8cf7f89930)
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