source: trunk/src/winmm/os2timer.cpp@ 10269

Last change on this file since 10269 was 10269, checked in by sandervl, 22 years ago

Make sure the timer object is not deleted inside the timer callback handler

File size: 14.4 KB
Line 
1/* $Id: os2timer.cpp,v 1.22 2003-10-13 09:18:37 sandervl Exp $ */
2
3/*
4 * OS/2 Timer class
5 *
6 * Copyright 1998 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 1999 Patrick Haller (phaller@gmx.net)
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12
13
14/****************************************************************************
15 * Includes *
16 ****************************************************************************/
17
18#define INCL_DOSPROCESS
19#define INCL_DOSDATETIME
20#define INCL_DOSSEMAPHORES
21#include <os2wrap.h> //Odin32 OS/2 api wrappers
22#include <process.h>
23#include <win32type.h>
24#include <win32api.h>
25#include <wprocess.h>
26#include <dbglog.h>
27#include <vmutex.h>
28
29#include "time.h"
30#include "os2timer.h"
31
32#define DBG_LOCALLOG DBG_os2timer
33#include "dbglocal.h"
34
35
36/****************************************************************************
37 * Structures *
38 ****************************************************************************/
39static VMutex timeMutex;
40
41/****************************************************************************
42 * Local Prototypes *
43 ****************************************************************************/
44
45static DWORD WIN32API TimerHlpHandler(LPVOID timer);
46
47
48
49/****************************************************************************
50 * Class: OS2TimerResolution *
51 ****************************************************************************/
52
53
54/*****************************************************************************
55 * Name : OS2TimerResolution::OS2TimerResolution
56 * Purpose : create a new entry in the resolution stack
57 * Parameters: -
58 * Variables :
59 * Result :
60 * Remark :
61 * Status : UNTESTED STUB
62 *
63 * Author : Patrick Haller [Tue, 1998/06/16 23:00]
64 *****************************************************************************/
65
66OS2TimerResolution::OS2TimerResolution(int dwPeriod)
67 : next(NULL)
68{
69 dprintf2(("WINMM:OS2Timer: OS2TimerResolution::OS2TimerResolution(%08xh,%08xh)\n",
70 this,
71 dwPeriod));
72
73 timeMutex.enter();
74 // add to linked list
75 OS2TimerResolution *timeRes = OS2TimerResolution::sTimerResolutions;
76
77 if(timeRes != NULL)
78 {
79 while(timeRes->next != NULL)
80 {
81 timeRes = timeRes->next;
82 }
83 timeRes->next = this;
84 }
85 else
86 OS2TimerResolution::sTimerResolutions = this;
87
88 timeMutex.leave();
89
90 this->dwPeriod = dwPeriod;
91}
92
93
94/*****************************************************************************
95 * Name : OS2TimerResolution::~OS2TimerResolution
96 * Purpose : remove entry from the linked list
97 * Parameters:
98 * Variables :
99 * Result :
100 * Remark :
101 * Status : UNTESTED STUB
102 *
103 * Author : Patrick Haller [Tue, 1998/06/16 23:00]
104 *****************************************************************************/
105
106OS2TimerResolution::~OS2TimerResolution()
107{
108 dprintf2(("WINMM:OS2Timer: OS2TimerResolution::~OS2TimerResolution(%08xh)\n",
109 this));
110
111
112 timeMutex.enter();
113
114 // remove from linked list
115 OS2TimerResolution *timeRes = OS2TimerResolution::sTimerResolutions;
116
117 // leaveResolutionScope() if still entered???
118
119 if(timeRes != this)
120 {
121 while(timeRes->next != this)
122 {
123 timeRes = timeRes->next;
124 }
125 timeRes->next = this->next;
126 }
127 else
128 OS2TimerResolution::sTimerResolutions = timeRes->next;
129
130 timeMutex.leave();
131}
132
133
134/*****************************************************************************
135 * Name : OS2TimerResolution::enterResolutionScope
136 * Purpose : set the currently requested timer resolution for this entry
137 * Parameters:
138 * Variables :
139 * Result :
140 * Remark :
141 * Status : UNTESTED STUB
142 *
143 * Author : Patrick Haller [Tue, 1998/06/16 23:00]
144 *****************************************************************************/
145
146BOOL OS2TimerResolution::enterResolutionScope(int dwPeriod)
147{
148 dprintf2(("WINMM:OS2Timer: OS2TimerResolution::enterResolutionScope(%08xh)\n",
149 dwPeriod));
150
151 OS2TimerResolution* timeRes = new OS2TimerResolution(dwPeriod);
152 if (timeRes != NULL)
153 return TRUE;
154 else
155 return FALSE;
156}
157
158
159/*****************************************************************************
160 * Name : OS2TimerResolution::leaveResolutionScope
161 * Purpose : remove specified entry from the list if periods match
162 * Parameters: int dwPeriod
163 * Variables :
164 * Result : TRUE or FALSE
165 * Remark :
166 * Status : UNTESTED STUB
167 *
168 * Author : Patrick Haller [Tue, 1998/06/16 23:00]
169 *****************************************************************************/
170
171BOOL OS2TimerResolution::leaveResolutionScope(int dwPeriod)
172{
173 dprintf2(("WINMM:OS2Timer: OS2TimerResolution::leaveResolutionScope(%08xh)\n",
174 dwPeriod));
175
176 timeMutex.enter();
177 OS2TimerResolution* timeRes = OS2TimerResolution::sTimerResolutions;
178
179 if (timeRes != NULL)
180 {
181 for(; // walk to the end of the list
182 timeRes->next != NULL;
183 timeRes = timeRes->next)
184 ;
185
186 if (timeRes->dwPeriod == dwPeriod) // do the requested period match?
187 {
188 delete timeRes; // so delete that object
189 timeMutex.leave();
190 return TRUE; // OK, can remove the entry
191 }
192 }
193 timeMutex.leave();
194
195 return FALSE; // nope, mismatch !
196}
197
198
199/*****************************************************************************
200 * Name : OS2TimerResolution::queryCurrentResolution
201 * Purpose : determine the minimum resolution currently requested
202 * Parameters:
203 * Variables :
204 * Result :
205 * Remark :
206 * Status : UNTESTED STUB
207 *
208 * Author : Patrick Haller [Tue, 1998/06/16 23:00]
209 *****************************************************************************/
210
211int OS2TimerResolution::queryCurrentResolution()
212{
213 timeMutex.enter();
214 OS2TimerResolution *timeRes = OS2TimerResolution::sTimerResolutions;
215 int iMin = -1;
216
217 if (timeRes != NULL) // do we have an entry yet?
218 for (; // walk the linked list
219 timeRes->next != NULL;
220 timeRes = timeRes->next)
221 {
222 if (timeRes->dwPeriod < iMin) // determine minimum time period
223 iMin = timeRes->dwPeriod;
224 }
225 timeMutex.leave();
226
227 dprintf(("WINMM:OS2Timer: OS2TimerResolution::queryCurrentResolution == %08xh\n",
228 iMin));
229
230 return iMin;
231}
232
233
234
235
236
237/******************************************************************************/
238/******************************************************************************/
239OS2Timer::OS2Timer() : TimerSem(0), TimerHandle(0), hTimerThread(0),
240 clientCallback(NULL), TimerStatus(Stopped), fFatal(FALSE),
241 next(NULL), timerID(0), refCount(0)
242{
243 dprintf(("WINMM:OS2Timer: OS2Timer::OS2Timer(%08xh)\n",
244 this));
245
246 timeMutex.enter();
247 OS2Timer *timer = OS2Timer::timers;
248
249 if(timer != NULL)
250 {
251 while(timer->next != NULL)
252 {
253 timer = timer->next;
254 }
255 timer->next = this;
256 }
257 else
258 timers = this;
259 timeMutex.leave();
260
261 // create timer semaphore
262 int rc = DosCreateEventSem(NULL, &TimerSem, DC_SEM_SHARED, 0);
263 if(rc != 0)
264 {
265 dprintf(("WINMM: OS2Timer: DosCreateEventSem failed rc=#%08xh\n", rc));
266 return; // terminate thread
267 }
268
269 //increase reference count for creation
270 addRef();
271
272 //increase reference count since the thread will access the object
273 addRef();
274 hTimerThread = CreateThread(NULL,
275 0x4000,
276 TimerHlpHandler,
277 (LPVOID)this,
278 0, // thread creation flags
279 &TimerThreadID);
280
281 if (hTimerThread == NULL)
282 {
283 dprintf(("WINMM: OS2Timer: CreateThread failed rc=#%08xh\n",
284 GetLastError()));
285 DosCloseEventSem(TimerSem);
286 }
287}
288/******************************************************************************/
289/******************************************************************************/
290OS2Timer::~OS2Timer()
291{
292 dprintf(("WINMM:OS2Timer: OS2Timer::~OS2Timer(%08xh)\n",
293 this));
294
295 KillTimer();
296
297 timeMutex.enter();
298 OS2Timer *timer = OS2Timer::timers;
299
300 if(timer != this)
301 {
302 while(timer->next != this)
303 {
304 timer = timer->next;
305 }
306 timer->next = this->next;
307 }
308 else
309 timers = timer->next;
310
311 timeMutex.leave();
312}
313/******************************************************************************/
314/******************************************************************************/
315BOOL OS2Timer::StartTimer(int period,
316 int resolution,
317 LPTIMECALLBACK lptc,
318 int dwUser,
319 int fuEvent)
320{
321 dprintf(("WINMM:OS2Timer: OS2Timer::StartTimer(%08xh, %08xh, %08xh, %08xh, %08xh, %08xh)\n",
322 this,
323 period,
324 resolution,
325 lptc,
326 dwUser,
327 fuEvent));
328
329 APIRET rc;
330
331 // has the thread been created properly?
332 if(hTimerThread == NULLHANDLE)
333 return(FALSE);
334
335 if(TimerStatus == Stopped)
336 {
337 clientCallback = lptc; // callback data (id / addr)
338 userData = dwUser; // user-supplied data
339 dwFlags = fuEvent; // type of timer / callback
340
341 if(fuEvent & TIME_PERIODIC)
342 rc = DosStartTimer(period, (HSEM)TimerSem, &TimerHandle);
343 else
344 rc = DosAsyncTimer(period, (HSEM)TimerSem, &TimerHandle);
345
346 if(rc)
347 {
348
349#ifdef DEBUG
350 if(fuEvent & TIME_PERIODIC)
351 WriteLog("DosStartTimer failed %d\n", rc);
352 else
353 WriteLog("DosAsyncTimer failed %d\n", rc);
354#endif
355
356 return(FALSE);
357 }
358
359 TimerStatus = Running;
360 }
361 else
362 return(FALSE); //already running (must use timeKillEvent first)
363
364 return(TRUE);
365}
366/******************************************************************************/
367/******************************************************************************/
368void OS2Timer::StopTimer()
369{
370 dprintf(("WINMM:OS2Timer: OS2Timer::StopTimer(%08xh)\n",
371 this));
372
373 if(TimerStatus == Running)
374 {
375 DosStopTimer(TimerHandle);
376 TimerStatus = Stopped;
377 }
378}
379/******************************************************************************/
380/******************************************************************************/
381void OS2Timer::KillTimer()
382{
383 dprintf(("WINMM:OS2Timer: OS2Timer::KillTimer(%08xh)\n",
384 this));
385
386 fFatal = TRUE;
387
388 StopTimer();
389
390 if(DosPostEventSem(TimerSem))
391 {
392 //something went wrong, kill the thread
393 TerminateThread(hTimerThread, -1);
394 }
395 TimerStatus = InActive;
396}
397//******************************************************************************
398//******************************************************************************
399#ifdef DEBUG
400LONG OS2Timer::addRef()
401{
402//// dprintf2(("addRef %x -> refcount %x", this, refCount+1));
403 return InterlockedIncrement(&refCount);
404};
405#endif
406/******************************************************************************/
407//******************************************************************************
408LONG OS2Timer::release()
409{
410#ifdef DEBUG
411 if(refCount-1 < 0) {
412 DebugInt3();
413 }
414#endif
415 if(InterlockedDecrement(&refCount) == 0) {
416 dprintf2(("marked for deletion -> delete now"));
417 delete this;
418 return 0;
419 }
420 return refCount;
421}
422/******************************************************************************/
423//******************************************************************************
424void OS2Timer::TimerHandler()
425{
426 ULONG Count = 0;
427 APIRET rc = 0; /* Return code */
428 USHORT selTIB;
429
430 dprintf(("WINMM: TimerHandler thread created (%08xh)\n",
431 this));
432
433 rc = DosSetPriority (PRTYS_THREAD, /* Change a single thread */
434 PRTYC_TIMECRITICAL, /* Time critical class */
435 0L, /* Increase by 15 */
436 0L); /* Assume current thread */
437
438 while(!fFatal)
439 {
440 dprintf(("WINMM: OS2Timer::TimerHandler waiting on timer (%04xh, %08xh\n",
441 dwFlags,
442 clientCallback));
443
444 rc = DosWaitEventSem(TimerSem, SEM_INDEFINITE_WAIT);
445 if(rc) {
446 dprintf(("DosWaitEventSem failed with %d", rc));
447 DebugInt3();
448 }
449 rc = DosResetEventSem(TimerSem, &Count);
450 if(rc) {
451 dprintf(("DosResetEventSem failed with %d", rc));
452 DebugInt3();
453 }
454 if(!fFatal)
455 {
456 // @@@PH: we're calling the client with PRTYC_TIMECRITICAL !!!
457 // It'd be much nicer to call with original priority!
458
459 // check timer running condition
460 if (TimerStatus == Running)
461 {
462 dprintf(("WINMM: OS2Timer::TimerHandler firing (%04xh, %08xh\n",
463 dwFlags,
464 clientCallback));
465
466 // process the event
467 switch (dwFlags & 0x0030)
468 {
469 case TIME_CALLBACK_FUNCTION:
470 if (clientCallback != NULL)
471 {
472 clientCallback((UINT)timerID, 0, userData, 0, 0);
473 }
474 break;
475
476 case TIME_CALLBACK_EVENT_SET:
477 SetEvent( (HANDLE)clientCallback );
478 break;
479
480 case TIME_CALLBACK_EVENT_PULSE:
481 PulseEvent( (HANDLE)clientCallback );
482 break;
483
484 default:
485 dprintf(("WINMM: OS2Timer %08xh: unknown type for mmtime callback(%08xh)\n",
486 this,
487 dwFlags));
488 }
489 }
490 }
491 }
492
493 // last message
494 dprintf(("WINMM: OS2Timer: Timer thread terminating (%08xh)\n",
495 this));
496
497 DosCloseEventSem(TimerSem);
498
499 //release object
500 release();
501
502 // mark this thread as terminated
503 ExitThread(0);
504}
505//******************************************************************************
506//******************************************************************************
507static DWORD WIN32API TimerHlpHandler(LPVOID timer)
508{
509 ((OS2Timer *)timer)->TimerHandler();
510
511 return 0;
512}
513//******************************************************************************
514//******************************************************************************
515OS2TimerResolution *OS2TimerResolution::sTimerResolutions = NULL;
516OS2Timer *OS2Timer::timers = NULL;
517int OS2Timer::timerPeriod = 0;
518
Note: See TracBrowser for help on using the repository browser.