source: trunk/src/helpers/timer.c@ 43

Last change on this file since 43 was 41, checked in by umoeller, 24 years ago

Updated timers.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1
2/*
3 *@@sourcefile timer.c:
4 * XTimers, which can be used to avoid excessive PM
5 * timer usage.
6 *
7 * These timers operate similar to PM timers started
8 * with WinStartTimer. These are implemented thru a
9 * separate thread (fntTimersThread) which posts
10 * WM_TIMER messages regularly.
11 *
12 * Instead of WinStartTimer, use tmrStartXTimer.
13 * Instead of WinStopTimer, use tmrStopXTimer.
14 *
15 * The main advantage of the XTimers is that these
16 * are not a limited resource (as opposed to PM timers).
17 * I don't know how many PM timers exist on the system,
18 * but PMREF says that the no. of remaining timers can
19 * be queried with WinQuerySysValue(SV_CTIMERS).
20 *
21 * There are a few limitations with the XTimers though:
22 *
23 * -- If you start a timer with a timeout < 100 ms,
24 * the first WM_TIMER might not appear before
25 * 100 ms have elapsed. This may or be not the
26 * case, depending on whether other timers are
27 * running.
28 *
29 * -- XTimers post WM_TIMER messages regardless of
30 * whether previous WM_TIMER messages have already
31 * been processed. For this reason, be careful with
32 * small timer timeouts, this might flood the
33 * message queue.
34 *
35 * -- Queue timers (with HWND == NULLHANDLE) are not
36 * supported.
37 *
38 * -- When a window is deleted, its timers are not
39 * automatically cleaned up. The timer thread does
40 * detect invalid windows and removes them from the
41 * timers list before posting, but to be on the safe
42 * side, always call tmrStopAllTimers when WM_DESTROY
43 * comes into a window which has used timers.
44 *
45 * Function prefixes:
46 * -- tmr* timer functions
47 *
48 *@@header "helpers\timer.h"
49 *@@added V0.9.7 (2000-12-04) [umoeller]
50 */
51
52/*
53 * Copyright (C) 2000 Ulrich M”ller.
54 * This file is part of the "XWorkplace helpers" source package.
55 * This is free software; you can redistribute it and/or modify
56 * it under the terms of the GNU General Public License as published
57 * by the Free Software Foundation, in version 2 as it comes in the
58 * "COPYING" file of the XWorkplace main distribution.
59 * This program is distributed in the hope that it will be useful,
60 * but WITHOUT ANY WARRANTY; without even the implied warranty of
61 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62 * GNU General Public License for more details.
63 */
64
65// OS2 includes
66
67#define OS2EMX_PLAIN_CHAR
68 // this is needed for "os2emx.h"; if this is defined,
69 // emx will define PSZ as _signed_ char, otherwise
70 // as unsigned char
71
72#define INCL_DOSEXCEPTIONS
73#define INCL_DOSPROCESS
74#define INCL_DOSSEMAPHORES
75#define INCL_DOSMISC
76#define INCL_DOSERRORS
77
78#define INCL_WINWINDOWMGR
79#define INCL_WINMESSAGEMGR
80#define INCL_WINTIMER
81#include <os2.h>
82
83#include <stdio.h>
84#include <setjmp.h>
85
86#include "setup.h" // code generation and debugging options
87
88#include "helpers\datetime.h"
89#include "helpers\except.h"
90#include "helpers\linklist.h"
91#include "helpers\threads.h"
92#include "helpers\timer.h"
93
94/*
95 *@@category: Helpers\PM helpers\Timer replacements
96 * see timer.c.
97 */
98
99/* ******************************************************************
100 *
101 * Private declarations
102 *
103 ********************************************************************/
104
105/*
106 *@@ XTIMER:
107 * one of these represents an XTimer.
108 * These are stored in a global linked list.
109 */
110
111typedef struct _XTIMER
112{
113 USHORT usTimerID; // timer ID, as passed to tmrStartXTimer
114 HWND hwndTarget; // target window, as passed to tmrStartXTimer
115 ULONG ulTimeout; // timer's timeout (in ms)
116 ULONG ulNextFire; // next time scalar (from dtGetULongTime) to fire at
117} XTIMER, *PXTIMER;
118
119/* ******************************************************************
120 *
121 * Global variables
122 *
123 ********************************************************************/
124
125// timers thread
126// HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex
127// THREADINFO G_tiTimers = {0}; // timers thread (only running
128 // if any timers were requested)
129// BOOL G_fTimersThreadRunning = FALSE;
130// LINKLIST G_llTimers; // linked list of XTIMER pointers
131
132/*
133 *@@ fntTimersThread:
134 * the actual thread which fires the timers by
135 * posting WM_TIMER messages to the respecive
136 * target windows when a timer has elapsed.
137 *
138 * This thread is dynamically started when the
139 * first timer is started thru tmrStartXTimer.
140 * It is automatically stopped (to be precise:
141 * it terminates itself) when the last timer
142 * is stopped thru tmrStopXTimer, which then
143 * sets the thread's fExit flag to TRUE.
144 *
145 *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
146 */
147
148/* void _Optlink fntTimersThread(PTHREADINFO ptiMyself)
149{
150 ULONG ulInterval = 25;
151 HAB hab = WinInitialize(0);
152 BOOL fLocked = FALSE;
153
154 // linked list of timers found to be invalid;
155 // this holds LISTNODE pointers from the global
156 // list to be removed
157 LINKLIST llInvalidTimers;
158 lstInit(&llInvalidTimers,
159 FALSE); // no auto-free
160
161 #ifdef __DEBUG__
162 DosBeep(3000, 30);
163 #endif
164
165 // keep running while we have timers
166 while (!ptiMyself->fExit)
167 {
168 // ULONG ulNesting = 0;
169
170 ULONG ulTimeNow;
171
172 DosSleep(ulInterval);
173
174 // minimum interval: 100 ms; this is lowered
175 // if we find any timers in the list which
176 // have a lower timeout to make sure we can
177 // fire at a lower interval...
178 ulInterval = 100;
179
180 // DosEnterMustComplete(&ulNesting);
181
182 TRY_LOUD(excpt1)
183 {
184 fLocked = LockTimers();
185 if (fLocked)
186 {
187 } // end if (fLocked)
188 }
189 CATCH(excpt1) { } END_CATCH();
190
191 if (fLocked)
192 {
193 UnlockTimers();
194 fLocked = FALSE;
195 }
196
197 // DosExitMustComplete(&ulNesting);
198
199 } // end while (!ptiMyself->fExit)
200
201 WinTerminate(hab);
202
203 #ifdef __DEBUG__
204 DosBeep(1500, 30);
205 #endif
206} */
207
208/* ******************************************************************
209 *
210 * Private functions
211 *
212 ********************************************************************/
213
214/*
215 *@@ FindTimer:
216 * returns the XTIMER structure from the global
217 * linked list which matches the given window
218 * _and_ timer ID.
219 *
220 * Internal function.
221 */
222
223PXTIMER FindTimer(PXTIMERSET pSet,
224 HWND hwnd,
225 USHORT usTimerID)
226{
227 if (pSet && pSet->pvllXTimers)
228 {
229 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers;
230 PLISTNODE pNode = lstQueryFirstNode(pllXTimers);
231 while (pNode)
232 {
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;
242 }
243 }
244
245 return (NULL);
246}
247
248/*
249 *@@ RemoveTimer:
250 * removes the specified XTIMER structure from
251 * the global linked list of running timers.
252 *
253 * Internal function.
254 *
255 * Preconditions:
256 *
257 * -- The caller must call LockTimers() first.
258 */
259
260VOID 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 }
273}
274
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
291PXTIMERSET 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
325VOID 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
366VOID 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:
481 * starts an XTimer.
482 *
483 * Any window can request an XTimer using
484 * this function. This operates similar to
485 * WinStartTimer, except that the number of
486 * XTimers is not limited.
487 *
488 * Returns a new timer or resets an existing
489 * timer (if usTimerID is already used with
490 * hwnd). Use tmrStopXTimer to stop the timer.
491 *
492 * The timer is _not_ stopped automatically
493 * when the window is destroyed.
494 *
495 *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
496 */
497
498USHORT XWPENTRY tmrStartXTimer(PXTIMERSET pSet,
499 HWND hwnd,
500 USHORT usTimerID,
501 ULONG ulTimeout)
502{
503 USHORT usrc = 0;
504
505 // _Pmpf((__FUNCTION__ ": entering"));
506
507 if (pSet && pSet->pvllXTimers)
508 {
509 PLINKLIST pllXTimers = (PLINKLIST)pSet->pvllXTimers;
510
511 if ((hwnd) && (ulTimeout))
512 {
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));
533 if (pTimer)
534 {
535 ULONG ulTimeNow;
536 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
537 &ulTimeNow, sizeof(ulTimeNow));
538 pTimer->usTimerID = usTimerID;
539 pTimer->hwndTarget = hwnd;
540 pTimer->ulTimeout = ulTimeout;
541 pTimer->ulNextFire = ulTimeNow + ulTimeout;
542
543 lstAppendItem(pllXTimers,
544 pTimer);
545 usrc = pTimer->usTimerID;
546
547 // _Pmpf((" new timer created"));
548 }
549 }
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));
565
566 return (usrc);
567}
568
569/*
570 *@@ tmrStopXTimer:
571 * similar to WinStopTimer, this stops the
572 * specified timer (which must have been
573 * started with the same hwnd and usTimerID
574 * using tmrStartXTimer).
575 *
576 * Returns TRUE if the timer was stopped.
577 */
578
579BOOL XWPENTRY tmrStopXTimer(PXTIMERSET pSet,
580 HWND hwnd,
581 USHORT usTimerID)
582{
583 BOOL brc = FALSE;
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)
593 {
594 RemoveTimer(pSet, pTimer);
595 brc = TRUE;
596 }
597 }
598
599 return (brc);
600}
601
602
Note: See TracBrowser for help on using the repository browser.