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

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

winmm: Restore normal priority when calling timer funciton.

This should prevent the callback code from working at the high
priority used for waiting on timer signals.

File size: 14.3 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
426 dprintf(("WINMM: TimerHandler thread created (%08xh)\n",
427 this));
428
429 // save the current thread priority
430 PTIB ptib = NULL;
431 DosGetInfoBlocks(&ptib, NULL);
432 ULONG prio = ptib->tib_ptib2->tib2_ulpri;
433
434 // increase the thread priority to improve timer resolution
435 DosSetPriority (PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
436
437 while(!fFatal)
438 {
439 dprintf(("WINMM: OS2Timer::TimerHandler waiting on timer (%04xh, %08xh\n",
440 dwFlags,
441 clientCallback));
442
443 rc = DosWaitEventSem(TimerSem, SEM_INDEFINITE_WAIT);
444 if(rc) {
445 dprintf(("DosWaitEventSem failed with %d", rc));
446 DebugInt3();
447 }
448 rc = DosResetEventSem(TimerSem, &Count);
449 if(rc) {
450 dprintf(("DosResetEventSem failed with %d", rc));
451 DebugInt3();
452 }
453 if(!fFatal)
454 {
455 // check timer running condition
456 if (TimerStatus == Running)
457 {
458 dprintf(("WINMM: OS2Timer::TimerHandler firing (%04xh, %08xh\n",
459 dwFlags,
460 clientCallback));
461
462 // process the event
463 switch (dwFlags & 0x0030)
464 {
465 case TIME_CALLBACK_FUNCTION:
466 if (clientCallback != NULL)
467 {
468 // restore the original priority (we never know what the callback
469 // code does)
470 DosSetPriority (PRTYS_THREAD, (prio >> 8) & 0xFF, 0, 0);
471
472 clientCallback((UINT)timerID, 0, userData, 0, 0);
473
474 DosSetPriority (PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
475 }
476 break;
477
478 case TIME_CALLBACK_EVENT_SET:
479 SetEvent( (HANDLE)clientCallback );
480 break;
481
482 case TIME_CALLBACK_EVENT_PULSE:
483 PulseEvent( (HANDLE)clientCallback );
484 break;
485
486 default:
487 dprintf(("WINMM: OS2Timer %08xh: unknown type for mmtime callback(%08xh)\n",
488 this,
489 dwFlags));
490 }
491 }
492 }
493 }
494
495 // last message
496 dprintf(("WINMM: OS2Timer: Timer thread terminating (%08xh)\n",
497 this));
498
499 DosCloseEventSem(TimerSem);
500
501 //release object
502 release();
503}
504//******************************************************************************
505//******************************************************************************
506static DWORD WIN32API TimerHlpHandler(LPVOID timer)
507{
508 ((OS2Timer *)timer)->TimerHandler();
509
510 return 0;
511}
512//******************************************************************************
513//******************************************************************************
514OS2TimerResolution *OS2TimerResolution::sTimerResolutions = NULL;
515OS2Timer *OS2Timer::timers = NULL;
516int OS2Timer::timerPeriod = 0;
517
Note: See TracBrowser for help on using the repository browser.