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 34struct deTimer_s 35{ 36 deTimerCallback callback; 37 void* callbackArg; 38 39 HANDLE timer; 40}; 41 42static 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 50deTimer* 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 64void 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 74deBool deTimer_isActive (const deTimer* timer) 75{ 76 return timer->timer != 0; 77} 78 79deBool 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 99deBool 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 119void 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 160struct deTimer_s 161{ 162 deTimerCallback callback; 163 void* callbackArg; 164 165 timer_t timer; 166 167 deBool isActive; 168}; 169 170static void timerCallback (union sigval val) 171{ 172 const deTimer* timer = (const deTimer*)val.sival_ptr; 173 timer->callback(timer->callbackArg); 174} 175 176deTimer* 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 202void deTimer_destroy (deTimer* timer) 203{ 204 DE_ASSERT(timer); 205 206 timer_delete(timer->timer); 207 deFree(timer); 208} 209 210deBool deTimer_isActive (const deTimer* timer) 211{ 212 return timer->isActive; 213} 214 215deBool 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 236deBool 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 257void 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 283typedef 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 292typedef 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 304struct deTimer_s 305{ 306 deTimerCallback callback; /*!< Callback function. */ 307 void* callbackArg; /*!< User pointer. */ 308 deTimerThread* curThread; /*!< Current timer thread. */ 309}; 310 311static 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 355static 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 381deTimer* 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 394void deTimer_destroy (deTimer* timer) 395{ 396 if (timer->curThread) 397 deTimer_disable(timer); 398 deFree(timer); 399} 400 401deBool 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 417deBool 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 428deBool 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 439void 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