Changeset 41 for trunk/src/helpers/timer.c
- Timestamp:
- Mar 3, 2001, 11:58:48 AM (24 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/helpers/timer.c
r21 r41 2 2 /* 3 3 *@@sourcefile timer.c: 4 * XTimers, which can replace the PM timers. 4 * XTimers, which can be used to avoid excessive PM 5 * timer usage. 5 6 * 6 7 * These timers operate similar to PM timers started … … 9 10 * WM_TIMER messages regularly. 10 11 * 11 * Instead of WinStartTimer, use tmrStart Timer.12 * Instead of WinStopTimer, use tmrStop Timer.12 * Instead of WinStartTimer, use tmrStartXTimer. 13 * Instead of WinStopTimer, use tmrStopXTimer. 13 14 * 14 15 * The main advantage of the XTimers is that these … … 75 76 #define INCL_DOSERRORS 76 77 78 #define INCL_WINWINDOWMGR 77 79 #define INCL_WINMESSAGEMGR 80 #define INCL_WINTIMER 78 81 #include <os2.h> 79 82 … … 108 111 typedef struct _XTIMER 109 112 { 110 USHORT usTimerID; // timer ID, as passed to tmrStart Timer111 HWND hwndTarget; // target window, as passed to tmrStart Timer113 USHORT usTimerID; // timer ID, as passed to tmrStartXTimer 114 HWND hwndTarget; // target window, as passed to tmrStartXTimer 112 115 ULONG ulTimeout; // timer's timeout (in ms) 113 116 ULONG ulNextFire; // next time scalar (from dtGetULongTime) to fire at … … 121 124 122 125 // timers thread 123 HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex124 THREADINFO G_tiTimers = {0}; // timers thread (only running126 // HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex 127 // THREADINFO G_tiTimers = {0}; // timers thread (only running 125 128 // if any timers were requested) 126 BOOL G_fTimersThreadRunning = FALSE; 127 LINKLIST G_llTimers; // linked list of XTIMER pointers 128 129 /* ****************************************************************** 130 * 131 * Timer helpers 132 * 133 ********************************************************************/ 134 135 /* 136 *@@ LockTimers: 137 * locks the global timer resources. 138 * You MUST call UnlockTimers after this. 139 */ 140 141 BOOL LockTimers(VOID) 142 { 143 BOOL brc = FALSE; 144 if (G_hmtxTimers == NULLHANDLE) 145 { 146 // this initializes all globals 147 lstInit(&G_llTimers, 148 TRUE); // auto-free 149 150 brc = (DosCreateMutexSem(NULL, // unnamed 151 &G_hmtxTimers, 152 0, // unshared 153 TRUE) // lock! 154 == NO_ERROR); 155 } 156 else 157 brc = (WinRequestMutexSem(G_hmtxTimers, SEM_INDEFINITE_WAIT) 158 == NO_ERROR); 159 return (brc); 160 } 161 162 /* 163 *@@ UnlockTimers: 164 * the reverse to LockTimers. 165 */ 166 167 VOID UnlockTimers(VOID) 168 { 169 DosReleaseMutexSem(G_hmtxTimers); 170 } 129 // BOOL G_fTimersThreadRunning = FALSE; 130 // LINKLIST G_llTimers; // linked list of XTIMER pointers 171 131 172 132 /* … … 177 137 * 178 138 * This thread is dynamically started when the 179 * first timer is started thru tmrStart Timer.139 * first timer is started thru tmrStartXTimer. 180 140 * It is automatically stopped (to be precise: 181 141 * it terminates itself) when the last timer 182 * is stopped thru tmrStop Timer, which then142 * is stopped thru tmrStopXTimer, which then 183 143 * sets the thread's fExit flag to TRUE. 184 144 * … … 186 146 */ 187 147 188 void _Optlink fntTimersThread(PTHREADINFO ptiMyself)148 /* void _Optlink fntTimersThread(PTHREADINFO ptiMyself) 189 149 { 190 150 ULONG ulInterval = 25; … … 225 185 if (fLocked) 226 186 { 227 // go thru all XTimers and see which one228 // has elapsed; for all of these, post WM_TIMER229 // to the target window proc230 PLISTNODE pTimerNode = lstQueryFirstNode(&G_llTimers);231 if (!pTimerNode)232 // no more timers left:233 // terminate thread234 ptiMyself->fExit = TRUE;235 else236 {237 // we have timers:238 BOOL fFoundInvalid = FALSE;239 240 // get current time241 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,242 &ulTimeNow, sizeof(ulTimeNow));243 244 while ((pTimerNode) && (!ptiMyself->fExit))245 {246 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;247 248 if (pTimer->ulNextFire < ulTimeNow)249 {250 // this timer has elapsed:251 // fire!252 if (WinIsWindow(hab,253 pTimer->hwndTarget))254 {255 // window still valid:256 WinPostMsg(pTimer->hwndTarget,257 WM_TIMER,258 (MPARAM)pTimer->usTimerID,259 0);260 pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout;261 }262 else263 {264 // window has been destroyed:265 #ifdef __DEBUG__266 DosBeep(100, 100);267 #endif268 lstAppendItem(&llInvalidTimers,269 (PVOID)pTimerNode);270 fFoundInvalid = TRUE;271 }272 } // end if (pTimer->ulNextFire < ulTimeNow)273 274 // adjust DosSleep interval275 if (pTimer->ulTimeout < ulInterval)276 ulInterval = pTimer->ulTimeout;277 278 // next timer279 pTimerNode = pTimerNode->pNext;280 } // end while (pTimerNode)281 282 // destroy invalid timers, if any283 if ((fFoundInvalid) && (!ptiMyself->fExit))284 {285 PLISTNODE pNodeNode = lstQueryFirstNode(&llInvalidTimers);286 while (pNodeNode)287 {288 PLISTNODE pNode2Remove = (PLISTNODE)pNodeNode->pItemData;289 lstRemoveNode(&G_llTimers,290 pNode2Remove);291 pNodeNode = pNodeNode->pNext;292 }293 lstClear(&llInvalidTimers);294 }295 } // end else if (!pTimerNode)296 187 } // end if (fLocked) 297 188 } … … 313 204 DosBeep(1500, 30); 314 205 #endif 315 } 206 } */ 207 208 /* ****************************************************************** 209 * 210 * Private functions 211 * 212 ********************************************************************/ 316 213 317 214 /* … … 322 219 * 323 220 * Internal function. 324 * 325 * Preconditions: 326 * 327 * -- The caller must call LockTimers() first. 328 */ 329 330 PXTIMER FindTimer(HWND hwnd, 221 */ 222 223 PXTIMER FindTimer(PXTIMERSET pSet, 224 HWND hwnd, 331 225 USHORT usTimerID) 332 226 { 333 PLISTNODE pNode = lstQueryFirstNode(&G_llTimers); 334 while (pNode) 335 { 336 PXTIMER pTimer = (PXTIMER)pNode->pItemData; 337 if ( (pTimer->usTimerID == usTimerID) 338 && (pTimer->hwndTarget == hwnd) 339 ) 227 if (pSet && pSet->pvllXTimers) 228 { 229 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 230 PLISTNODE pNode = lstQueryFirstNode(pllXTimers); 231 while (pNode) 340 232 { 341 return (pTimer); 233 PXTIMER pTimer = (PXTIMER)pNode->pItemData; 234 if ( (pTimer->usTimerID == usTimerID) 235 && (pTimer->hwndTarget == hwnd) 236 ) 237 { 238 return (pTimer); 239 } 240 241 pNode = pNode->pNext; 342 242 } 343 344 pNode = pNode->pNext;345 243 } 244 346 245 return (NULL); 347 246 } … … 359 258 */ 360 259 361 VOID RemoveTimer(PXTIMER pTimer) 362 { 363 lstRemoveItem(&G_llTimers, 364 pTimer); // auto-free! 365 /* if (lstCountItems(&G_llTimers) == 0) 366 // no more timers left: 367 // stop the main timer 368 thrClose(&G_tiTimers); */ 260 VOID RemoveTimer(PXTIMERSET pSet, 261 PXTIMER pTimer) 262 { 263 if (pSet && pSet->pvllXTimers) 264 { 265 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 266 lstRemoveItem(pllXTimers, 267 pTimer); // auto-free! 268 /* if (lstCountItems(&G_llTimers) == 0) 269 // no more timers left: 270 // stop the main timer 271 thrClose(&G_tiTimers); */ 272 } 369 273 } 370 274 371 /* 372 *@@ tmrStartTimer: 275 /* ****************************************************************** 276 * 277 * Exported functions 278 * 279 ********************************************************************/ 280 281 /* 282 *@@ tmrCreateSet: 283 * creates a "timer set" for use with the XTimer functions. 284 * This is the first step if you want to use the XTimers. 285 * 286 * Use tmrDestroySet to free all resources again. 287 * 288 *@@added V0.9.9 (2001-02-28) [umoeller] 289 */ 290 291 PXTIMERSET tmrCreateSet(HWND hwndOwner, 292 USHORT usPMTimerID) 293 { 294 PXTIMERSET pSet = NULL; 295 296 // _Pmpf((__FUNCTION__ ": entering")); 297 298 pSet = (PXTIMERSET)malloc(sizeof(*pSet)); 299 if (pSet) 300 { 301 pSet->hab = WinQueryAnchorBlock(hwndOwner); 302 pSet->hwndOwner = hwndOwner; 303 pSet->idPMTimer = usPMTimerID; 304 pSet->idPMTimerRunning = 0; 305 306 pSet->pvllXTimers = (PVOID)lstCreate(TRUE); 307 } 308 309 // _Pmpf((__FUNCTION__ ": leaving, returning 0x%lX", pSet)); 310 311 return (pSet); 312 } 313 314 /* 315 *@@ tmrDestroySet: 316 * destroys a timer set previously created using 317 * tmrCreateSet. 318 * 319 * Of course, this will stop all XTimers which 320 * might still be running with this set. 321 * 322 *@@added V0.9.9 (2001-02-28) [umoeller] 323 */ 324 325 VOID tmrDestroySet(PXTIMERSET pSet) 326 { 327 // _Pmpf((__FUNCTION__ ": entering")); 328 329 if (pSet) 330 { 331 if (pSet->pvllXTimers) 332 { 333 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 334 335 PLISTNODE pTimerNode; 336 337 while (pTimerNode = lstQueryFirstNode(pllXTimers)) 338 { 339 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData; 340 RemoveTimer(pSet, pTimer); 341 } 342 343 lstFree(pllXTimers); 344 } 345 346 free(pSet); 347 } 348 349 // _Pmpf((__FUNCTION__ ": leaving")); 350 } 351 352 /* 353 *@@ tmrTimerTick: 354 * implements a PM timer tick. 355 * 356 * When your window procs receives WM_TIMER for the 357 * one PM timer which is supposed to trigger all the 358 * XTimers, it must call this function. This will 359 * evaluate all XTimers on the list and "fire" them 360 * by calling the window procs directly with the 361 * WM_TIMER message. 362 * 363 *@@added V0.9.9 (2001-02-28) [umoeller] 364 */ 365 366 VOID tmrTimerTick(PXTIMERSET pSet) 367 { 368 // _Pmpf((__FUNCTION__ ": entering")); 369 370 if (pSet && pSet->pvllXTimers) 371 { 372 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 373 // go thru all XTimers and see which one 374 // has elapsed; for all of these, post WM_TIMER 375 // to the target window proc 376 PLISTNODE pTimerNode = lstQueryFirstNode(pllXTimers); 377 378 // stop the PM timer for now; we'll restart it later 379 WinStopTimer(pSet->hab, 380 pSet->hwndOwner, 381 pSet->idPMTimer); 382 pSet->idPMTimerRunning = 0; 383 384 if (pTimerNode) 385 { 386 // we have timers: 387 BOOL fFoundInvalid = FALSE; 388 389 ULONG ulInterval = 100, 390 ulTimeNow = 0; 391 392 // linked list of timers found to be invalid; 393 // this holds LISTNODE pointers from the global 394 // list to be removed 395 LINKLIST llInvalidTimers; 396 lstInit(&llInvalidTimers, 397 FALSE); // no auto-free 398 399 // get current time 400 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, 401 &ulTimeNow, sizeof(ulTimeNow)); 402 403 while (pTimerNode) 404 { 405 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData; 406 407 if (pTimer->ulNextFire < ulTimeNow) 408 { 409 // this timer has elapsed: 410 // fire! 411 if (WinIsWindow(pSet->hab, 412 pTimer->hwndTarget)) 413 { 414 // window still valid: 415 // get the window's window proc 416 PFNWP pfnwp = (PFNWP)WinQueryWindowPtr(pTimer->hwndTarget, 417 QWP_PFNWP); 418 // call the window proc DIRECTLY 419 pfnwp(pTimer->hwndTarget, 420 WM_TIMER, 421 (MPARAM)pTimer->usTimerID, 422 0); 423 pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout; 424 } 425 else 426 { 427 // window has been destroyed: 428 lstAppendItem(&llInvalidTimers, 429 (PVOID)pTimerNode); 430 fFoundInvalid = TRUE; 431 } 432 } // end if (pTimer->ulNextFire < ulTimeNow) 433 434 // adjust DosSleep interval 435 if (pTimer->ulTimeout < ulInterval) 436 ulInterval = pTimer->ulTimeout; 437 438 // next timer 439 pTimerNode = pTimerNode->pNext; 440 } // end while (pTimerNode) 441 442 // destroy invalid timers, if any 443 if (fFoundInvalid) 444 { 445 PLISTNODE pNodeNode = lstQueryFirstNode(&llInvalidTimers); 446 while (pNodeNode) 447 { 448 PLISTNODE pNode2Remove = (PLISTNODE)pNodeNode->pItemData; 449 lstRemoveNode(pllXTimers, 450 pNode2Remove); 451 pNodeNode = pNodeNode->pNext; 452 } 453 lstClear(&llInvalidTimers); 454 } 455 456 if (lstCountItems(pllXTimers)) 457 { 458 // any timers left: 459 // restart timer with the timeout calculated above 460 pSet->idPMTimerRunning = WinStartTimer(pSet->hab, 461 pSet->hwndOwner, 462 pSet->idPMTimer, 463 ulInterval); 464 /* _Pmpf((" %d timers left, restarted PM timer == %d, interval %d", 465 lstCountItems(pllXTimers), 466 pSet->idPMTimerRunning, 467 ulInterval)); 468 _Pmpf((" pSet->hab: 0x%lX, hwndOwner = 0x%lX, usPMTimerID = %d", 469 pSet->hab, 470 pSet->hwndOwner, 471 pSet->idPMTimer)); */ 472 } 473 } // end else if (!pTimerNode) 474 } 475 476 // _Pmpf((__FUNCTION__ ": leaving")); 477 } 478 479 /* 480 *@@ tmrStartXTimer: 373 481 * starts an XTimer. 374 482 * … … 380 488 * Returns a new timer or resets an existing 381 489 * timer (if usTimerID is already used with 382 * hwnd). Use tmrStop Timer to stop the timer.490 * hwnd). Use tmrStopXTimer to stop the timer. 383 491 * 384 492 * The timer is _not_ stopped automatically … … 388 496 */ 389 497 390 USHORT APIENTRY tmrStartTimer(HWND hwnd, 391 USHORT usTimerID, 392 ULONG ulTimeout) 498 USHORT XWPENTRY tmrStartXTimer(PXTIMERSET pSet, 499 HWND hwnd, 500 USHORT usTimerID, 501 ULONG ulTimeout) 393 502 { 394 503 USHORT usrc = 0; 395 BOOL fLocked = FALSE; 396 397 if ((hwnd) && (ulTimeout)) 398 {399 ULONG ulNesting = 0;400 DosEnterMustComplete(&ulNesting);401 402 TRY_LOUD(excpt1)504 505 // _Pmpf((__FUNCTION__ ": entering")); 506 507 if (pSet && pSet->pvllXTimers) 508 { 509 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 510 511 if ((hwnd) && (ulTimeout)) 403 512 { 404 fLocked = LockTimers(); 405 if (fLocked) 406 { 407 PXTIMER pTimer; 408 409 // if the timers thread is not yet running, 410 // start it now (i.e. this is the first timer) 411 if (!G_fTimersThreadRunning) 412 { 413 // main timer not yet running: 414 thrCreate(&G_tiTimers, 415 fntTimersThread, 416 &G_fTimersThreadRunning, 417 THRF_WAIT, // no msgq, but wait 418 0); 419 // raise priority 420 DosSetPriority(PRTYS_THREAD, 421 PRTYC_REGULAR, // 3 422 PRTYD_MAXIMUM, 423 G_tiTimers.tid); 424 } 425 426 // check if this timer exists already 427 pTimer = FindTimer(hwnd, 428 usTimerID); 513 PXTIMER pTimer; 514 515 // check if this timer exists already 516 pTimer = FindTimer(pSet, 517 hwnd, 518 usTimerID); 519 if (pTimer) 520 { 521 // exists already: reset only 522 ULONG ulTimeNow; 523 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, 524 &ulTimeNow, sizeof(ulTimeNow)); 525 pTimer->ulNextFire = ulTimeNow + ulTimeout; 526 usrc = pTimer->usTimerID; 527 // _Pmpf((" timer existed, reset")); 528 } 529 else 530 { 531 // new timer needed: 532 pTimer = (PXTIMER)malloc(sizeof(XTIMER)); 429 533 if (pTimer) 430 534 { 431 // exists already: reset only432 535 ULONG ulTimeNow; 433 536 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, 434 537 &ulTimeNow, sizeof(ulTimeNow)); 538 pTimer->usTimerID = usTimerID; 539 pTimer->hwndTarget = hwnd; 540 pTimer->ulTimeout = ulTimeout; 435 541 pTimer->ulNextFire = ulTimeNow + ulTimeout; 542 543 lstAppendItem(pllXTimers, 544 pTimer); 436 545 usrc = pTimer->usTimerID; 437 } 438 else 439 { 440 // new timer needed: 441 pTimer = (PXTIMER)malloc(sizeof(XTIMER)); 442 if (pTimer) 443 { 444 ULONG ulTimeNow; 445 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, 446 &ulTimeNow, sizeof(ulTimeNow)); 447 pTimer->usTimerID = usTimerID; 448 pTimer->hwndTarget = hwnd; 449 pTimer->ulTimeout = ulTimeout; 450 pTimer->ulNextFire = ulTimeNow + ulTimeout; 451 452 lstAppendItem(&G_llTimers, 453 pTimer); 454 usrc = pTimer->usTimerID; 455 } 546 547 // _Pmpf((" new timer created")); 456 548 } 457 549 } 458 } 459 CATCH(excpt1) { } END_CATCH(); 460 461 // unlock the sems outside the exception handler 462 if (fLocked) 463 { 464 UnlockTimers(); 465 fLocked = FALSE; 466 } 467 468 DosExitMustComplete(&ulNesting); 469 } // if ((hwnd) && (ulTimeout)) 550 551 if (usrc) 552 { 553 // timer created or reset: 554 // start main PM timer 555 pSet->idPMTimerRunning = WinStartTimer(pSet->hab, 556 pSet->hwndOwner, 557 pSet->idPMTimer, 558 100); 559 // _Pmpf((" started PM timer %d", pSet->idPMTimerRunning)); 560 } 561 } // if ((hwnd) && (ulTimeout)) 562 } 563 564 // _Pmpf((__FUNCTION__ ": leaving, returning %d", usrc)); 470 565 471 566 return (usrc); … … 473 568 474 569 /* 475 *@@ tmrStop Timer:570 *@@ tmrStopXTimer: 476 571 * similar to WinStopTimer, this stops the 477 572 * specified timer (which must have been 478 573 * started with the same hwnd and usTimerID 479 * using tmrStart Timer).574 * using tmrStartXTimer). 480 575 * 481 576 * Returns TRUE if the timer was stopped. 482 577 */ 483 578 484 BOOL APIENTRY tmrStopTimer(HWND hwnd, 485 USHORT usTimerID) 579 BOOL XWPENTRY tmrStopXTimer(PXTIMERSET pSet, 580 HWND hwnd, 581 USHORT usTimerID) 486 582 { 487 583 BOOL brc = FALSE; 488 BOOL fLocked = FALSE;489 490 ULONG ulNesting = 0;491 DosEnterMustComplete(&ulNesting);492 493 TRY_LOUD(excpt1)494 {495 fLocked = LockTimers();496 if ( fLocked)584 if (pSet && pSet->pvllXTimers) 585 { 586 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers; 587 BOOL fLocked = FALSE; 588 589 PXTIMER pTimer = FindTimer(pSet, 590 hwnd, 591 usTimerID); 592 if (pTimer) 497 593 { 498 PXTIMER pTimer = FindTimer(hwnd, 499 usTimerID); 500 if (pTimer) 501 { 502 RemoveTimer(pTimer); 503 brc = TRUE; 504 } 594 RemoveTimer(pSet, pTimer); 595 brc = TRUE; 505 596 } 506 597 } 507 CATCH(excpt1) { } END_CATCH();508 509 // unlock the sems outside the exception handler510 if (fLocked)511 {512 UnlockTimers();513 fLocked = FALSE;514 }515 516 DosExitMustComplete(&ulNesting);517 598 518 599 return (brc); 519 600 } 520 601 521 /* 522 *@@ tmrStopAllTimers: 523 * stops all timers which are running for the 524 * specified window. This is a useful helper 525 * that you should call during WM_DESTROY of 526 * a window that has started timers. 527 */ 528 529 VOID tmrStopAllTimers(HWND hwnd) 530 { 531 BOOL fLocked = FALSE; 532 533 ULONG ulNesting = 0; 534 DosEnterMustComplete(&ulNesting); 535 536 TRY_LOUD(excpt1) 537 { 538 fLocked = LockTimers(); 539 if (fLocked) 540 { 541 PLISTNODE pTimerNode = lstQueryFirstNode(&G_llTimers); 542 while (pTimerNode) 543 { 544 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData; 545 if (pTimer->hwndTarget == hwnd) 546 { 547 RemoveTimer(pTimer); 548 // start over 549 pTimerNode = lstQueryFirstNode(&G_llTimers); 550 } 551 else 552 pTimerNode = pTimerNode->pNext; 553 } 554 } 555 } 556 CATCH(excpt1) { } END_CATCH(); 557 558 // unlock the sems outside the exception handler 559 if (fLocked) 560 { 561 UnlockTimers(); 562 fLocked = FALSE; 563 } 564 565 DosExitMustComplete(&ulNesting); 566 } 567 568 602
Note:
See TracChangeset
for help on using the changeset viewer.