1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
4 * MMSYSTEM time functions
6 * Copyright 1993 Martin Ayotte
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/port.h"
28 #ifdef HAVE_SYS_TIME_H
29 # include <sys/time.h>
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(mmtime
);
45 static HANDLE TIME_hMMTimer
;
46 static LPWINE_TIMERENTRY TIME_TimersList
;
47 static HANDLE TIME_hKillEvent
;
48 DWORD WINMM_SysTimeMS
;
52 * We're using "1" as the mininum resolution to the timer,
53 * as Windows 95 does, according to the docs. Maybe it should
54 * depend on the computers resources!
56 #define MMSYSTIME_MININTERVAL (1)
57 #define MMSYSTIME_MAXINTERVAL (65535)
59 #define MMSYSTIME_STDINTERVAL (10) /* reasonable value? */
61 static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer
)
63 TRACE("before CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX !\n",
64 lpTimer
->lpFunc
, lpTimer
->wTimerID
, lpTimer
->dwUser
);
66 /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called
67 * during interrupt time, is allowed to execute very limited number of API calls (like
68 * PostMessage), and must reside in DLL (therefore uses stack of active application). So I
69 * guess current implementation via SetTimer has to be improved upon.
71 switch (lpTimer
->wFlags
& 0x30) {
72 case TIME_CALLBACK_FUNCTION
:
73 if (lpTimer
->wFlags
& WINE_TIMER_IS32
)
74 (lpTimer
->lpFunc
)(lpTimer
->wTimerID
, 0, lpTimer
->dwUser
, 0, 0);
75 else if (pFnCallMMDrvFunc16
)
76 pFnCallMMDrvFunc16((DWORD
)lpTimer
->lpFunc
, lpTimer
->wTimerID
, 0,
77 lpTimer
->dwUser
, 0, 0);
79 case TIME_CALLBACK_EVENT_SET
:
80 SetEvent((HANDLE
)lpTimer
->lpFunc
);
82 case TIME_CALLBACK_EVENT_PULSE
:
83 PulseEvent((HANDLE
)lpTimer
->lpFunc
);
86 FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n",
87 lpTimer
->wFlags
, lpTimer
->lpFunc
);
90 TRACE("after CallBack !\n");
93 /**************************************************************************
94 * TIME_MMSysTimeCallback
96 static void CALLBACK
TIME_MMSysTimeCallback(LPWINE_MM_IDATA iData
)
98 static int nSizeLpTimers
;
99 static LPWINE_TIMERENTRY lpTimers
;
101 LPWINE_TIMERENTRY timer
, *ptimer
, *next_ptimer
;
102 DWORD delta
= GetTickCount() - WINMM_SysTimeMS
;
105 TRACE("Time delta: %ld\n", delta
);
107 while (delta
>= MMSYSTIME_MININTERVAL
) {
108 delta
-= MMSYSTIME_MININTERVAL
;
109 WINMM_SysTimeMS
+= MMSYSTIME_MININTERVAL
;
111 /* since timeSetEvent() and timeKillEvent() can be called
112 * from 16 bit code, there are cases where win16 lock is
113 * locked upon entering timeSetEvent(), and then the mm timer
114 * critical section is locked. This function cannot call the
115 * timer callback with the crit sect locked (because callback
116 * may need to acquire Win16 lock, thus providing a deadlock
118 * To cope with that, we just copy the WINE_TIMERENTRY struct
119 * that need to trigger the callback, and call it without the
120 * mm timer crit sect locked.
121 * the hKillTimeEvent is used to mark the section where we
122 * handle the callbacks so we can do synchronous kills.
123 * EPP 99/07/13, updated 04/01/10
127 EnterCriticalSection(&iData
->cs
);
128 for (ptimer
= &TIME_TimersList
; *ptimer
!= NULL
; ) {
130 next_ptimer
= &timer
->lpNext
;
131 if (timer
->uCurTime
< MMSYSTIME_MININTERVAL
) {
132 /* since lpTimer->wDelay is >= MININTERVAL, wCurTime value
133 * shall be correct (>= 0)
135 timer
->uCurTime
+= timer
->wDelay
- MMSYSTIME_MININTERVAL
;
137 if (idx
== nSizeLpTimers
) {
139 lpTimers
= (LPWINE_TIMERENTRY
)
140 HeapReAlloc(GetProcessHeap(), 0, lpTimers
,
141 ++nSizeLpTimers
* sizeof(WINE_TIMERENTRY
));
143 lpTimers
= (LPWINE_TIMERENTRY
)
144 HeapAlloc(GetProcessHeap(), 0,
145 ++nSizeLpTimers
* sizeof(WINE_TIMERENTRY
));
147 lpTimers
[idx
++] = *timer
;
149 /* TIME_ONESHOT is defined as 0 */
150 if (!(timer
->wFlags
& TIME_PERIODIC
))
152 /* unlink timer from timers list */
153 *ptimer
= *next_ptimer
;
154 HeapFree(GetProcessHeap(), 0, timer
);
157 timer
->uCurTime
-= MMSYSTIME_MININTERVAL
;
159 ptimer
= next_ptimer
;
161 if (TIME_hKillEvent
) ResetEvent(TIME_hKillEvent
);
162 LeaveCriticalSection(&iData
->cs
);
164 while (idx
> 0) TIME_TriggerCallBack(&lpTimers
[--idx
]);
165 if (TIME_hKillEvent
) SetEvent(TIME_hKillEvent
);
169 /**************************************************************************
170 * TIME_MMSysTimeThread
172 static DWORD CALLBACK
TIME_MMSysTimeThread(LPVOID arg
)
174 LPWINE_MM_IDATA iData
= (LPWINE_MM_IDATA
)arg
;
175 volatile HANDLE
*pActive
= (volatile HANDLE
*)&TIME_hMMTimer
;
176 DWORD last_time
, cur_time
;
178 usleep(MMSYSTIME_STDINTERVAL
* 1000);
179 last_time
= GetTickCount();
181 TIME_MMSysTimeCallback(iData
);
182 cur_time
= GetTickCount();
183 while (last_time
< cur_time
)
184 last_time
+= MMSYSTIME_STDINTERVAL
;
185 usleep((last_time
- cur_time
) * 1000);
190 /**************************************************************************
193 void TIME_MMTimeStart(void)
195 /* one could think it's possible to stop the service thread activity when no more
196 * mm timers are active, but this would require to keep mmSysTimeMS up-to-date
197 * without being incremented within the service thread callback.
199 if (!TIME_hMMTimer
) {
200 WINMM_SysTimeMS
= GetTickCount();
201 TIME_TimersList
= NULL
;
202 TIME_hMMTimer
= CreateThread(NULL
, 0, TIME_MMSysTimeThread
, WINMM_IData
, 0, NULL
);
206 /**************************************************************************
209 void TIME_MMTimeStop(void)
211 /* FIXME: in the worst case, we're going to wait 65 seconds here :-( */
213 HANDLE hMMTimer
= TIME_hMMTimer
;
215 WaitForSingleObject(hMMTimer
, INFINITE
);
216 CloseHandle(hMMTimer
);
220 /**************************************************************************
221 * timeGetSystemTime [WINMM.@]
223 MMRESULT WINAPI
timeGetSystemTime(LPMMTIME lpTime
, UINT wSize
)
225 TRACE("(%p, %u);\n", lpTime
, wSize
);
227 if (wSize
>= sizeof(*lpTime
)) {
229 lpTime
->wType
= TIME_MS
;
230 lpTime
->u
.ms
= WINMM_SysTimeMS
;
232 TRACE("=> %lu\n", lpTime
->u
.ms
);
238 /**************************************************************************
239 * TIME_SetEventInternal [internal]
241 WORD
TIME_SetEventInternal(UINT wDelay
, UINT wResol
,
242 LPTIMECALLBACK lpFunc
, DWORD dwUser
, UINT wFlags
)
245 LPWINE_TIMERENTRY lpNewTimer
;
246 LPWINE_TIMERENTRY lpTimer
;
248 TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay
, wResol
, lpFunc
, dwUser
, wFlags
);
250 lpNewTimer
= (LPWINE_TIMERENTRY
)HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY
));
251 if (lpNewTimer
== NULL
)
254 if (wDelay
< MMSYSTIME_MININTERVAL
|| wDelay
> MMSYSTIME_MAXINTERVAL
)
259 lpNewTimer
->uCurTime
= wDelay
;
260 lpNewTimer
->wDelay
= wDelay
;
261 lpNewTimer
->wResol
= wResol
;
262 lpNewTimer
->lpFunc
= lpFunc
;
263 lpNewTimer
->dwUser
= dwUser
;
264 lpNewTimer
->wFlags
= wFlags
;
266 EnterCriticalSection(&WINMM_IData
->cs
);
268 if ((wFlags
& TIME_KILL_SYNCHRONOUS
) && !TIME_hKillEvent
)
269 TIME_hKillEvent
= CreateEventW(NULL
, TRUE
, TRUE
, NULL
);
271 for (lpTimer
= TIME_TimersList
; lpTimer
!= NULL
; lpTimer
= lpTimer
->lpNext
) {
272 wNewID
= max(wNewID
, lpTimer
->wTimerID
);
275 lpNewTimer
->lpNext
= TIME_TimersList
;
276 TIME_TimersList
= lpNewTimer
;
277 lpNewTimer
->wTimerID
= wNewID
+ 1;
279 LeaveCriticalSection(&WINMM_IData
->cs
);
281 TRACE("=> %u\n", wNewID
+ 1);
286 /**************************************************************************
287 * timeSetEvent [WINMM.@]
289 MMRESULT WINAPI
timeSetEvent(UINT wDelay
, UINT wResol
, LPTIMECALLBACK lpFunc
,
290 DWORD_PTR dwUser
, UINT wFlags
)
292 if (wFlags
& WINE_TIMER_IS32
)
293 WARN("Unknown windows flag... wine internally used.. ooch\n");
295 return TIME_SetEventInternal(wDelay
, wResol
, lpFunc
,
296 dwUser
, wFlags
|WINE_TIMER_IS32
);
299 /**************************************************************************
300 * timeKillEvent [WINMM.@]
302 MMRESULT WINAPI
timeKillEvent(UINT wID
)
304 LPWINE_TIMERENTRY lpSelf
= NULL
, *lpTimer
;
306 TRACE("(%u)\n", wID
);
307 EnterCriticalSection(&WINMM_IData
->cs
);
308 /* remove WINE_TIMERENTRY from list */
309 for (lpTimer
= &TIME_TimersList
; *lpTimer
; lpTimer
= &(*lpTimer
)->lpNext
) {
310 if (wID
== (*lpTimer
)->wTimerID
) {
312 /* unlink timer of id 'wID' */
313 *lpTimer
= (*lpTimer
)->lpNext
;
317 LeaveCriticalSection(&WINMM_IData
->cs
);
321 WARN("wID=%u is not a valid timer ID\n", wID
);
322 return MMSYSERR_INVALPARAM
;
324 if (lpSelf
->wFlags
& TIME_KILL_SYNCHRONOUS
)
325 WaitForSingleObject(TIME_hKillEvent
, INFINITE
);
326 HeapFree(GetProcessHeap(), 0, lpSelf
);
327 return TIMERR_NOERROR
;
330 /**************************************************************************
331 * timeGetDevCaps [WINMM.@]
333 MMRESULT WINAPI
timeGetDevCaps(LPTIMECAPS lpCaps
, UINT wSize
)
335 TRACE("(%p, %u) !\n", lpCaps
, wSize
);
337 lpCaps
->wPeriodMin
= MMSYSTIME_MININTERVAL
;
338 lpCaps
->wPeriodMax
= MMSYSTIME_MAXINTERVAL
;
342 /**************************************************************************
343 * timeBeginPeriod [WINMM.@]
345 MMRESULT WINAPI
timeBeginPeriod(UINT wPeriod
)
347 TRACE("(%u) !\n", wPeriod
);
349 if (wPeriod
< MMSYSTIME_MININTERVAL
|| wPeriod
> MMSYSTIME_MAXINTERVAL
)
350 return TIMERR_NOCANDO
;
354 /**************************************************************************
355 * timeEndPeriod [WINMM.@]
357 MMRESULT WINAPI
timeEndPeriod(UINT wPeriod
)
359 TRACE("(%u) !\n", wPeriod
);
361 if (wPeriod
< MMSYSTIME_MININTERVAL
|| wPeriod
> MMSYSTIME_MAXINTERVAL
)
362 return TIMERR_NOCANDO
;
366 /**************************************************************************
367 * timeGetTime [MMSYSTEM.607]
368 * timeGetTime [WINMM.@]
370 DWORD WINAPI
timeGetTime(void)
373 /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
374 * that lets mciavi.drv run correctly
376 if (pFnReleaseThunkLock
) pFnReleaseThunkLock(&count
);
378 if (pFnRestoreThunkLock
) pFnRestoreThunkLock(count
);
379 return WINMM_SysTimeMS
;