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

Last change on this file since 21930 was 21930, checked in by dmik, 14 years ago

winmm: Disable forceful termination of timer threads.

This is absolutely unnecessary and may cause resource leaks
eventually leading to the application hang or crash.

The above symptoms are known to happen in Adobe Flash applications
which use a lot of timers which are started/stopped at a very high rate.
This fix gets rid of these symptoms.

File size: 14.2 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 DosPostEventSem(TimerSem);
391
392 TimerStatus = InActive;
393}
394//******************************************************************************
395//******************************************************************************
396#ifdef DEBUG
397LONG OS2Timer::addRef()
398{
399//// dprintf2(("addRef %x -> refcount %x", this, refCount+1));
400 return InterlockedIncrement(&refCount);
401};
402#endif
403/******************************************************************************/
404//******************************************************************************
405LONG OS2Timer::release()
406{
407#ifdef DEBUG
408 if(refCount-1 < 0) {
409 DebugInt3();
410 }
411#endif
412 if(InterlockedDecrement(&refCount) == 0) {
413 dprintf2(("marked for deletion -> delete now"));
414 delete this;
415 return 0;
416 }
417 return refCount;
418}
419/******************************************************************************/
420//******************************************************************************
421void OS2Timer::TimerHandler()
422{
423 ULONG Count = 0;
424 APIRET rc = 0; /* Return code */
425 USHORT selTIB;
426
427 dprintf(("WINMM: TimerHandler thread created (%08xh)\n",
428 this));
429
430 rc = DosSetPriority (PRTYS_THREAD, /* Change a single thread */
431 PRTYC_TIMECRITICAL, /* Time critical class */
432 0L, /* Increase by 15 */
433 0L); /* Assume current thread */
434
435 while(!fFatal)
436 {
437 dprintf(("WINMM: OS2Timer::TimerHandler waiting on timer (%04xh, %08xh\n",
438 dwFlags,
439 clientCallback));
440
441 rc = DosWaitEventSem(TimerSem, SEM_INDEFINITE_WAIT);
442 if(rc) {
443 dprintf(("DosWaitEventSem failed with %d", rc));
444 DebugInt3();
445 }
446 rc = DosResetEventSem(TimerSem, &Count);
447 if(rc) {
448 dprintf(("DosResetEventSem failed with %d", rc));
449 DebugInt3();
450 }
451 if(!fFatal)
452 {
453 // @@@PH: we're calling the client with PRTYC_TIMECRITICAL !!!
454 // It'd be much nicer to call with original priority!
455
456 // check timer running condition
457 if (TimerStatus == Running)
458 {
459 dprintf(("WINMM: OS2Timer::TimerHandler firing (%04xh, %08xh\n",
460 dwFlags,
461 clientCallback));
462
463 // process the event
464 switch (dwFlags & 0x0030)
465 {
466 case TIME_CALLBACK_FUNCTION:
467 if (clientCallback != NULL)
468 {
469 clientCallback((UINT)timerID, 0, userData, 0, 0);
470 }
471 break;
472
473 case TIME_CALLBACK_EVENT_SET:
474 SetEvent( (HANDLE)clientCallback );
475 break;
476
477 case TIME_CALLBACK_EVENT_PULSE:
478 PulseEvent( (HANDLE)clientCallback );
479 break;
480
481 default:
482 dprintf(("WINMM: OS2Timer %08xh: unknown type for mmtime callback(%08xh)\n",
483 this,
484 dwFlags));
485 }
486 }
487 }
488 }
489
490 // last message
491 dprintf(("WINMM: OS2Timer: Timer thread terminating (%08xh)\n",
492 this));
493
494 DosCloseEventSem(TimerSem);
495
496 //release object
497 release();
498}
499//******************************************************************************
500//******************************************************************************
501static DWORD WIN32API TimerHlpHandler(LPVOID timer)
502{
503 ((OS2Timer *)timer)->TimerHandler();
504
505 return 0;
506}
507//******************************************************************************
508//******************************************************************************
509OS2TimerResolution *OS2TimerResolution::sTimerResolutions = NULL;
510OS2Timer *OS2Timer::timers = NULL;
511int OS2Timer::timerPeriod = 0;
512
Note: See TracBrowser for help on using the repository browser.