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 472