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

Last change on this file since 15 was 15, checked in by umoeller, 25 years ago

Coupla bugfixes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1
2/*
3 *@@sourcefile timer.c:
4 * XTimers, which can replace the PM timers.
5 *
6 * These timers operate similar to PM timers started
7 * with WinStartTimer. These are implemented thru a
8 * separate thread (fntTimersThread) which posts
9 * WM_TIMER messages regularly.
10 *
11 * Instead of WinStartTimer, use tmrStartTimer.
12 * Instead of WinStopTimer, use tmrStopTimer.
13 *
14 * The main advantage of the XTimers is that these
15 * are not a limited resource (as opposed to PM timers).
16 * I don't know how many PM timers exist on the system,
17 * but PMREF says that the no. of remaining timers can
18 * be queried with WinQuerySysValue(SV_CTIMERS).
19 *
20 * There are a few limitations with the XTimers though:
21 *
22 * -- If you start a timer with a timeout < 100 ms,
23 * the first WM_TIMER might not appear before
24 * 100 ms have elapsed. This may or be not the
25 * case, depending on whether other timers are
26 * running.
27 *
28 * -- XTimers post WM_TIMER messages regardless of
29 * whether previous WM_TIMER messages have already
30 * been processed. For this reason, be careful with
31 * small timer timeouts, this might flood the
32 * message queue.
33 *
34 * -- Queue timers (with HWND == NULLHANDLE) are not
35 * supported.
36 *
37 * -- When a window is deleted, its timers are not
38 * automatically cleaned up. To be on the safe
39 * side, always call tmrStopAllTimers when
40 * WM_DESTROY comes into a window which has used
41 * timers.
42 *
43 * Function prefixes:
44 * -- tmr* timer functions
45 *
46 *@@header "helpers\timer.h"
47 *@@added V0.9.7 (2000-12-04) [umoeller]
48 */
49
50/*
51 * Copyright (C) 2000 Ulrich M”ller.
52 * This file is part of the "XWorkplace helpers" source package.
53 * This is free software; you can redistribute it and/or modify
54 * it under the terms of the GNU General Public License as published
55 * by the Free Software Foundation, in version 2 as it comes in the
56 * "COPYING" file of the XWorkplace main distribution.
57 * This program is distributed in the hope that it will be useful,
58 * but WITHOUT ANY WARRANTY; without even the implied warranty of
59 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
60 * GNU General Public License for more details.
61 */
62
63// OS2 includes
64
65#define OS2EMX_PLAIN_CHAR
66 // this is needed for "os2emx.h"; if this is defined,
67 // emx will define PSZ as _signed_ char, otherwise
68 // as unsigned char
69
70#define INCL_DOSEXCEPTIONS
71#define INCL_DOSPROCESS
72#define INCL_DOSSEMAPHORES
73#define INCL_DOSMISC
74#define INCL_DOSERRORS
75#include <os2.h>
76
77#include <stdio.h>
78#include <setjmp.h>
79
80#include "setup.h" // code generation and debugging options
81
82#include "helpers\datetime.h"
83#include "helpers\except.h"
84#include "helpers\linklist.h"
85#include "helpers\threads.h"
86#include "helpers\timer.h"
87
88/*
89 *@@category: Helpers\PM helpers\Timer replacements
90 */
91
92/* ******************************************************************
93 *
94 * Private declarations
95 *
96 ********************************************************************/
97
98/*
99 *@@ XTIMER:
100 * one of these represents an XTimer.
101 * These are stored in a global linked list.
102 */
103
104typedef struct _XTIMER
105{
106 USHORT usTimerID; // timer ID, as passed to tmrStartTimer
107 HWND hwndTarget; // target window, as passed to tmrStartTimer
108 ULONG ulTimeout; // timer's timeout (in ms)
109 ULONG ulNextFire; // next time scalar (from dtGetULongTime) to fire at
110} XTIMER, *PXTIMER;
111
112/* ******************************************************************
113 *
114 * Global variables
115 *
116 ********************************************************************/
117
118// timers thread
119HMTX G_hmtxTimers = NULLHANDLE; // timers lock mutex
120THREADINFO G_tiTimers = {0}; // timers thread (only running
121 // if any timers were requested)
122BOOL G_fTimersThreadRunning = FALSE;
123LINKLIST G_llTimers; // linked list of XTIMER pointers
124
125/* ******************************************************************
126 *
127 * Timer helpers
128 *
129 ********************************************************************/
130
131/*
132 *@@ LockTimers:
133 * locks the global timer resources.
134 * You MUST call UnlockTimers after this.
135 */
136
137BOOL LockTimers(VOID)
138{
139 BOOL brc = FALSE;
140 if (G_hmtxTimers == NULLHANDLE)
141 {
142 // this initializes all globals
143 lstInit(&G_llTimers,
144 TRUE); // auto-free
145
146 brc = (DosCreateMutexSem(NULL, // unnamed
147 &G_hmtxTimers,
148 0, // unshared
149 TRUE) // lock!
150 == NO_ERROR);
151 }
152 else
153 brc = (DosRequestMutexSem(G_hmtxTimers, SEM_INDEFINITE_WAIT)
154 == NO_ERROR);
155 return (brc);
156}
157
158/*
159 *@@ UnlockTimers:
160 * the reverse to LockTimers.
161 */
162
163VOID UnlockTimers(VOID)
164{
165 DosReleaseMutexSem(G_hmtxTimers);
166}
167
168/*
169 *@@ tmrOnKill:
170 * on-kill proc for exception handlers.
171 *
172 *@@added V0.9.7 (2000-12-09) [umoeller]
173 */
174
175VOID APIENTRY tmrOnKill(PEXCEPTIONREGISTRATIONRECORD2 pRegRec2)
176{
177 DosBeep(500, 500);
178 UnlockTimers();
179}
180
181/*
182 *@@ fntTimersThread:
183 * the actual thread which fires the timers by
184 * posting WM_TIMER messages to the respecive
185 * target windows when a timer has elapsed.
186 *
187 * This thread is dynamically started when the
188 * first timer is started thru tmrStartTimer.
189 * It is automatically stopped (to be precise:
190 * it terminates itself) when the last timer
191 * is stopped thru tmrStopTimer, which then
192 * sets the thread's fExit flag to TRUE.
193 *
194 *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
195 */
196
197void _Optlink fntTimersThread(PTHREADINFO ptiMyself)
198{
199 ULONG ulInterval = 25;
200 HAB hab = WinInitialize(0);
201 BOOL fLocked = FALSE;
202
203 // linked list of timers found to be invalid;
204 // this holds LISTNODE pointers from the global
205 // list to be removed
206 LINKLIST llInvalidTimers;
207 lstInit(&llInvalidTimers,
208 FALSE); // no auto-free
209
210 #ifdef __DEBUG__
211 DosBeep(3000, 30);
212 #endif
213
214 // keep running while we have timers
215 while (!ptiMyself->fExit)
216 {
217 ULONG ulNesting = 0;
218
219 DosSleep(ulInterval);
220
221 // minimum interval: 100 ms; this is lowered
222 // if we find any timers in the list which
223 // have a lower timeout to make sure we can
224 // fire at a lower interval...
225 ulInterval = 100;
226
227 TRY_LOUD(excpt1)
228 {
229 DosEnterMustComplete(&ulNesting);
230
231 fLocked = LockTimers();
232 if (fLocked)
233 {
234 // go thru all XTimers and see which one
235 // has elapsed; for all of these, post WM_TIMER
236 // to the target window proc
237 PLISTNODE pTimerNode = lstQueryFirstNode(&G_llTimers);
238 if (!pTimerNode)
239 // no more timers left:
240 // terminate thread
241 ptiMyself->fExit = TRUE;
242 else
243 {
244 // we have timers:
245 BOOL fFoundInvalid = FALSE;
246 while (pTimerNode)
247 {
248 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
249 ULONG ulTimeNow;
250 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
251 &ulTimeNow, sizeof(ulTimeNow));
252
253 if (pTimer->ulNextFire < ulTimeNow)
254 {
255 // this timer has elapsed:
256 // fire!
257 if (WinIsWindow(hab,
258 pTimer->hwndTarget))
259 {
260 // window still valid:
261 WinPostMsg(pTimer->hwndTarget,
262 WM_TIMER,
263 (MPARAM)pTimer->usTimerID,
264 0);
265 pTimer->ulNextFire = ulTimeNow + pTimer->ulTimeout;
266 }
267 else
268 {
269 // window has been destroyed:
270 #ifdef __DEBUG__
271 DosBeep(100, 100);
272 #endif
273 lstAppendItem(&llInvalidTimers,
274 (PVOID)pTimerNode);
275 fFoundInvalid = TRUE;
276 }
277 } // end if (pTimer->ulNextFire < ulTimeNow)
278
279 // adjust DosSleep interval
280 if (pTimer->ulTimeout < ulInterval)
281 ulInterval = pTimer->ulTimeout;
282
283 // next timer
284 pTimerNode = pTimerNode->pNext;
285 } // end while (pTimerNode)
286
287 // destroy invalid timers, if any
288 if (fFoundInvalid)
289 {
290 PLISTNODE pNodeNode = lstQueryFirstNode(&llInvalidTimers);
291 while (pNodeNode)
292 {
293 PLISTNODE pNode2Remove = (PLISTNODE)pNodeNode->pItemData;
294 lstRemoveNode(&G_llTimers,
295 pNode2Remove);
296 pNodeNode = pNodeNode->pNext;
297 }
298 lstClear(&llInvalidTimers);
299 }
300 } // end else if (!pTimerNode)
301 } // end if (fLocked)
302 }
303 CATCH(excpt1) { } END_CATCH();
304
305 if (fLocked)
306 {
307 UnlockTimers();
308 fLocked = FALSE;
309 }
310
311 DosExitMustComplete(&ulNesting);
312
313 } // end while (!ptiMyself->fExit)
314
315 WinTerminate(hab);
316
317 #ifdef __DEBUG__
318 DosBeep(1500, 30);
319 #endif
320}
321
322/*
323 *@@ FindTimer:
324 * returns the XTIMER structure from the global
325 * linked list which matches the given window
326 * _and_ timer ID.
327 *
328 * Internal function.
329 *
330 * Preconditions:
331 *
332 * -- The caller must call LockTimers() first.
333 */
334
335PXTIMER FindTimer(HWND hwnd,
336 USHORT usTimerID)
337{
338 PLISTNODE pNode = lstQueryFirstNode(&G_llTimers);
339 while (pNode)
340 {
341 PXTIMER pTimer = (PXTIMER)pNode->pItemData;
342 if ( (pTimer->usTimerID == usTimerID)
343 && (pTimer->hwndTarget == hwnd)
344 )
345 {
346 return (pTimer);
347 }
348
349 pNode = pNode->pNext;
350 }
351 return (NULL);
352}
353
354/*
355 *@@ RemoveTimer:
356 * removes the specified XTIMER structure from
357 * the global linked list of running timers.
358 *
359 * Internal function.
360 *
361 * Preconditions:
362 *
363 * -- The caller must call LockTimers() first.
364 */
365
366VOID RemoveTimer(PXTIMER pTimer)
367{
368 lstRemoveItem(&G_llTimers,
369 pTimer); // auto-free!
370 /* if (lstCountItems(&G_llTimers) == 0)
371 // no more timers left:
372 // stop the main timer
373 thrClose(&G_tiTimers); */
374}
375
376/*
377 *@@ tmrStartTimer:
378 * starts an XTimer.
379 *
380 * Any window can request an XTimer using
381 * this function. This operates similar to
382 * WinStartTimer, except that the number of
383 * XTimers is not limited.
384 *
385 * Returns a new timer or resets an existing
386 * timer (if usTimerID is already used with
387 * hwnd). Use tmrStopTimer to stop the timer.
388 *
389 * The timer is _not_ stopped automatically
390 * when the window is destroyed.
391 *
392 *@@changed V0.9.7 (2000-12-08) [umoeller]: got rid of dtGetULongTime
393 */
394
395USHORT APIENTRY tmrStartTimer(HWND hwnd,
396 USHORT usTimerID,
397 ULONG ulTimeout)
398{
399 USHORT usrc = 0;
400 BOOL fLocked = FALSE;
401
402 if ((hwnd) && (ulTimeout))
403 {
404 ULONG ulNesting = 0;
405 DosEnterMustComplete(&ulNesting);
406
407 TRY_LOUD(excpt1)
408 {
409 fLocked = LockTimers();
410 if (fLocked)
411 {
412 PXTIMER pTimer;
413
414 // if the timers thread is not yet running,
415 // start it now (i.e. this is the first timer)
416 if (!G_fTimersThreadRunning)
417 {
418 // main timer not yet running:
419 thrCreate(&G_tiTimers,
420 fntTimersThread,
421 &G_fTimersThreadRunning,
422 THRF_WAIT, // no msgq, but wait
423 0);
424 // raise priority
425 DosSetPriority(PRTYS_THREAD,
426 PRTYC_REGULAR, // 3
427 PRTYD_MAXIMUM,
428 G_tiTimers.tid);
429 }
430
431 // check if this timer exists already
432 pTimer = FindTimer(hwnd,
433 usTimerID);
434 if (pTimer)
435 {
436 // exists already: reset only
437 ULONG ulTimeNow;
438 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
439 &ulTimeNow, sizeof(ulTimeNow));
440 pTimer->ulNextFire = ulTimeNow + ulTimeout;
441 usrc = pTimer->usTimerID;
442 }
443 else
444 {
445 // new timer needed:
446 pTimer = (PXTIMER)malloc(sizeof(XTIMER));
447 if (pTimer)
448 {
449 ULONG ulTimeNow;
450 DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT,
451 &ulTimeNow, sizeof(ulTimeNow));
452 pTimer->usTimerID = usTimerID;
453 pTimer->hwndTarget = hwnd;
454 pTimer->ulTimeout = ulTimeout;
455 pTimer->ulNextFire = ulTimeNow + ulTimeout;
456
457 lstAppendItem(&G_llTimers,
458 pTimer);
459 usrc = pTimer->usTimerID;
460 }
461 }
462 }
463 }
464 CATCH(excpt1) { } END_CATCH();
465
466 // unlock the sems outside the exception handler
467 if (fLocked)
468 {
469 UnlockTimers();
470 fLocked = FALSE;
471 }
472
473 DosExitMustComplete(&ulNesting);
474 } // if ((hwnd) && (ulTimeout))
475
476 return (usrc);
477}
478
479/*
480 *@@ tmrStopTimer:
481 * similar to WinStopTimer, this stops the
482 * specified timer (which must have been
483 * started with the same hwnd and usTimerID
484 * using tmrStartTimer).
485 *
486 * Returns TRUE if the timer was stopped.
487 */
488
489BOOL APIENTRY tmrStopTimer(HWND hwnd,
490 USHORT usTimerID)
491{
492 BOOL brc = FALSE;
493 BOOL fLocked = FALSE;
494
495 ULONG ulNesting = 0;
496 DosEnterMustComplete(&ulNesting);
497
498 TRY_LOUD(excpt1)
499 {
500 fLocked = LockTimers();
501 if (fLocked)
502 {
503 PXTIMER pTimer = FindTimer(hwnd,
504 usTimerID);
505 if (pTimer)
506 {
507 RemoveTimer(pTimer);
508 brc = TRUE;
509 }
510 }
511 }
512 CATCH(excpt1) { } END_CATCH();
513
514 // unlock the sems outside the exception handler
515 if (fLocked)
516 {
517 UnlockTimers();
518 fLocked = FALSE;
519 }
520
521 DosExitMustComplete(&ulNesting);
522
523 return (brc);
524}
525
526/*
527 *@@ tmrStopAllTimers:
528 * stops all timers which are running for the
529 * specified window. This is a useful helper
530 * that you should call during WM_DESTROY of
531 * a window that has started timers.
532 */
533
534VOID tmrStopAllTimers(HWND hwnd)
535{
536 BOOL fLocked = FALSE;
537
538 ULONG ulNesting = 0;
539 DosEnterMustComplete(&ulNesting);
540
541 TRY_LOUD(excpt1)
542 {
543 fLocked = LockTimers();
544 if (fLocked)
545 {
546 PLISTNODE pTimerNode = lstQueryFirstNode(&G_llTimers);
547 while (pTimerNode)
548 {
549 PXTIMER pTimer = (PXTIMER)pTimerNode->pItemData;
550 if (pTimer->hwndTarget == hwnd)
551 {
552 RemoveTimer(pTimer);
553 // start over
554 pTimerNode = lstQueryFirstNode(&G_llTimers);
555 }
556 else
557 pTimerNode = pTimerNode->pNext;
558 }
559 }
560 }
561 CATCH(excpt1) { } END_CATCH();
562
563 // unlock the sems outside the exception handler
564 if (fLocked)
565 {
566 UnlockTimers();
567 fLocked = FALSE;
568 }
569
570 DosExitMustComplete(&ulNesting);
571}
572
573
Note: See TracBrowser for help on using the repository browser.