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 470