1c2c66affSColin Finck /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2c2c66affSColin Finck
3c2c66affSColin Finck /*
4c2c66affSColin Finck * MMSYSTEM time functions
5c2c66affSColin Finck *
6c2c66affSColin Finck * Copyright 1993 Martin Ayotte
7c2c66affSColin Finck *
8c2c66affSColin Finck * This library is free software; you can redistribute it and/or
9c2c66affSColin Finck * modify it under the terms of the GNU Lesser General Public
10c2c66affSColin Finck * License as published by the Free Software Foundation; either
11c2c66affSColin Finck * version 2.1 of the License, or (at your option) any later version.
12c2c66affSColin Finck *
13c2c66affSColin Finck * This library is distributed in the hope that it will be useful,
14c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of
15c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16c2c66affSColin Finck * Lesser General Public License for more details.
17c2c66affSColin Finck *
18c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public
19c2c66affSColin Finck * License along with this library; if not, write to the Free Software
20c2c66affSColin Finck * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21c2c66affSColin Finck */
22c2c66affSColin Finck
23c2c66affSColin Finck #include "winemm.h"
24c2c66affSColin Finck
25c2c66affSColin Finck #ifdef HAVE_SYS_TIME_H
26c2c66affSColin Finck # include <sys/time.h>
27c2c66affSColin Finck #endif
28c2c66affSColin Finck #ifdef HAVE_UNISTD_H
29c2c66affSColin Finck # include <unistd.h>
30c2c66affSColin Finck #endif
31c2c66affSColin Finck
32c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
33c2c66affSColin Finck
34c2c66affSColin Finck typedef struct tagWINE_TIMERENTRY {
35c2c66affSColin Finck UINT wDelay;
36c2c66affSColin Finck UINT wResol;
37c2c66affSColin Finck LPTIMECALLBACK lpFunc; /* can be lots of things */
38c2c66affSColin Finck DWORD dwUser;
39c2c66affSColin Finck UINT16 wFlags;
40c2c66affSColin Finck UINT16 wTimerID;
41c2c66affSColin Finck DWORD dwTriggerTime;
42c2c66affSColin Finck struct tagWINE_TIMERENTRY* lpNext;
43c2c66affSColin Finck } WINE_TIMERENTRY, *LPWINE_TIMERENTRY;
44c2c66affSColin Finck
45c2c66affSColin Finck static HANDLE TIME_hMMTimer;
46c2c66affSColin Finck static LPWINE_TIMERENTRY TIME_TimersList;
47c2c66affSColin Finck static HANDLE TIME_hKillEvent;
48c2c66affSColin Finck static HANDLE TIME_hWakeEvent;
49c2c66affSColin Finck static BOOL TIME_TimeToDie = TRUE;
50*88300ec3SThomas Brogan static LARGE_INTEGER TIME_qpcFreq;
51c2c66affSColin Finck
52c2c66affSColin Finck /*
53c2c66affSColin Finck * Some observations on the behavior of winmm on Windows.
54c2c66affSColin Finck * First, the call to timeBeginPeriod(xx) can never be used
55c2c66affSColin Finck * to raise the timer resolution, only lower it.
56c2c66affSColin Finck *
57c2c66affSColin Finck * Second, a brief survey of a variety of Win 2k and Win X
58c2c66affSColin Finck * machines showed that a 'standard' (aka default) timer
59c2c66affSColin Finck * resolution was 1 ms (Win9x is documented as being 1). However, one
60c2c66affSColin Finck * machine had a standard timer resolution of 10 ms.
61c2c66affSColin Finck *
62c2c66affSColin Finck * Further, if we set our default resolution to 1,
63c2c66affSColin Finck * the implementation of timeGetTime becomes GetTickCount(),
64c2c66affSColin Finck * and we can optimize the code to reduce overhead.
65c2c66affSColin Finck *
66c2c66affSColin Finck * Additionally, a survey of Event behaviors shows that
67c2c66affSColin Finck * if we request a Periodic event every 50 ms, then Windows
68c2c66affSColin Finck * makes sure to trigger that event 20 times in the next
69c2c66affSColin Finck * second. If delays prevent that from happening on exact
70c2c66affSColin Finck * schedule, Windows will trigger the events as close
71c2c66affSColin Finck * to the original schedule as is possible, and will eventually
72c2c66affSColin Finck * bring the event triggers back onto a schedule that is
73c2c66affSColin Finck * consistent with what would have happened if there were
74c2c66affSColin Finck * no delays.
75c2c66affSColin Finck *
76c2c66affSColin Finck * Jeremy White, October 2004
77c2c66affSColin Finck */
78c2c66affSColin Finck #define MMSYSTIME_MININTERVAL (1)
79c2c66affSColin Finck #define MMSYSTIME_MAXINTERVAL (65535)
80c2c66affSColin Finck
81c2c66affSColin Finck
TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)82c2c66affSColin Finck static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
83c2c66affSColin Finck {
84c2c66affSColin Finck TRACE("%04lx:CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX dwTriggerTime %ld(delta %ld)\n",
85c2c66affSColin Finck GetCurrentThreadId(), lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser,
86c2c66affSColin Finck lpTimer->dwTriggerTime, GetTickCount() - lpTimer->dwTriggerTime);
87c2c66affSColin Finck
88c2c66affSColin Finck /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called
89c2c66affSColin Finck * during interrupt time, is allowed to execute very limited number of API calls (like
90c2c66affSColin Finck * PostMessage), and must reside in DLL (therefore uses stack of active application). So I
91c2c66affSColin Finck * guess current implementation via SetTimer has to be improved upon.
92c2c66affSColin Finck */
93c2c66affSColin Finck switch (lpTimer->wFlags & 0x30) {
94c2c66affSColin Finck case TIME_CALLBACK_FUNCTION:
95c2c66affSColin Finck (lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
96c2c66affSColin Finck break;
97c2c66affSColin Finck case TIME_CALLBACK_EVENT_SET:
98c2c66affSColin Finck SetEvent((HANDLE)lpTimer->lpFunc);
99c2c66affSColin Finck break;
100c2c66affSColin Finck case TIME_CALLBACK_EVENT_PULSE:
101c2c66affSColin Finck PulseEvent((HANDLE)lpTimer->lpFunc);
102c2c66affSColin Finck break;
103c2c66affSColin Finck default:
104c2c66affSColin Finck FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n",
105c2c66affSColin Finck lpTimer->wFlags, lpTimer->lpFunc);
106c2c66affSColin Finck break;
107c2c66affSColin Finck }
108c2c66affSColin Finck }
109c2c66affSColin Finck
110c2c66affSColin Finck /**************************************************************************
111c2c66affSColin Finck * TIME_MMSysTimeCallback
112c2c66affSColin Finck */
TIME_MMSysTimeCallback()113c2c66affSColin Finck static DWORD CALLBACK TIME_MMSysTimeCallback()
114c2c66affSColin Finck {
115c2c66affSColin Finck static int nSizeLpTimers;
116c2c66affSColin Finck static LPWINE_TIMERENTRY lpTimers;
117c2c66affSColin Finck
118c2c66affSColin Finck LPWINE_TIMERENTRY timer, *ptimer, *next_ptimer;
119c2c66affSColin Finck int idx;
120c2c66affSColin Finck DWORD cur_time;
121c2c66affSColin Finck DWORD delta_time;
122c2c66affSColin Finck DWORD ret_time = INFINITE;
123c2c66affSColin Finck DWORD adjust_time;
124c2c66affSColin Finck
125c2c66affSColin Finck
126c2c66affSColin Finck /* optimize for the most frequent case - no events */
127c2c66affSColin Finck if (! TIME_TimersList)
128c2c66affSColin Finck return(ret_time);
129c2c66affSColin Finck
130c2c66affSColin Finck /* since timeSetEvent() and timeKillEvent() can be called
131c2c66affSColin Finck * from 16 bit code, there are cases where win16 lock is
132c2c66affSColin Finck * locked upon entering timeSetEvent(), and then the mm timer
133c2c66affSColin Finck * critical section is locked. This function cannot call the
134c2c66affSColin Finck * timer callback with the crit sect locked (because callback
135c2c66affSColin Finck * may need to acquire Win16 lock, thus providing a deadlock
136c2c66affSColin Finck * situation).
137c2c66affSColin Finck * To cope with that, we just copy the WINE_TIMERENTRY struct
138c2c66affSColin Finck * that need to trigger the callback, and call it without the
139c2c66affSColin Finck * mm timer crit sect locked.
140c2c66affSColin Finck * the hKillTimeEvent is used to mark the section where we
141c2c66affSColin Finck * handle the callbacks so we can do synchronous kills.
142c2c66affSColin Finck * EPP 99/07/13, updated 04/01/10
143c2c66affSColin Finck */
144c2c66affSColin Finck idx = 0;
145c2c66affSColin Finck cur_time = GetTickCount();
146c2c66affSColin Finck
147c2c66affSColin Finck EnterCriticalSection(&WINMM_cs);
148c2c66affSColin Finck for (ptimer = &TIME_TimersList; *ptimer != NULL; ) {
149c2c66affSColin Finck timer = *ptimer;
150c2c66affSColin Finck next_ptimer = &timer->lpNext;
151c2c66affSColin Finck if (cur_time >= timer->dwTriggerTime)
152c2c66affSColin Finck {
153c2c66affSColin Finck if (timer->lpFunc) {
154c2c66affSColin Finck if (idx == nSizeLpTimers) {
155c2c66affSColin Finck if (lpTimers)
156c2c66affSColin Finck lpTimers = (LPWINE_TIMERENTRY)
157c2c66affSColin Finck HeapReAlloc(GetProcessHeap(), 0, lpTimers,
158c2c66affSColin Finck ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
159c2c66affSColin Finck else
160c2c66affSColin Finck lpTimers = (LPWINE_TIMERENTRY)
161c2c66affSColin Finck HeapAlloc(GetProcessHeap(), 0,
162c2c66affSColin Finck ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
163c2c66affSColin Finck }
164c2c66affSColin Finck lpTimers[idx++] = *timer;
165c2c66affSColin Finck
166c2c66affSColin Finck }
167c2c66affSColin Finck
168c2c66affSColin Finck /* Update the time after we make the copy to preserve
169c2c66affSColin Finck the original trigger time */
170c2c66affSColin Finck timer->dwTriggerTime += timer->wDelay;
171c2c66affSColin Finck
172c2c66affSColin Finck /* TIME_ONESHOT is defined as 0 */
173c2c66affSColin Finck if (!(timer->wFlags & TIME_PERIODIC))
174c2c66affSColin Finck {
175c2c66affSColin Finck /* unlink timer from timers list */
176c2c66affSColin Finck *ptimer = *next_ptimer;
177c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, timer);
178c2c66affSColin Finck
179c2c66affSColin Finck /* We don't need to trigger oneshots again */
180c2c66affSColin Finck delta_time = INFINITE;
181c2c66affSColin Finck }
182c2c66affSColin Finck else
183c2c66affSColin Finck {
184c2c66affSColin Finck /* Compute when this event needs this function
185c2c66affSColin Finck to be called again */
186c2c66affSColin Finck if (timer->dwTriggerTime <= cur_time)
187c2c66affSColin Finck delta_time = 0;
188c2c66affSColin Finck else
189c2c66affSColin Finck delta_time = timer->dwTriggerTime - cur_time;
190c2c66affSColin Finck }
191c2c66affSColin Finck }
192c2c66affSColin Finck else
193c2c66affSColin Finck delta_time = timer->dwTriggerTime - cur_time;
194c2c66affSColin Finck
195c2c66affSColin Finck /* Determine when we need to return to this function */
196c2c66affSColin Finck ret_time = min(ret_time, delta_time);
197c2c66affSColin Finck
198c2c66affSColin Finck ptimer = next_ptimer;
199c2c66affSColin Finck }
200c2c66affSColin Finck if (TIME_hKillEvent) ResetEvent(TIME_hKillEvent);
201c2c66affSColin Finck LeaveCriticalSection(&WINMM_cs);
202c2c66affSColin Finck
203c2c66affSColin Finck while (idx > 0) TIME_TriggerCallBack(&lpTimers[--idx]);
204c2c66affSColin Finck if (TIME_hKillEvent) SetEvent(TIME_hKillEvent);
205c2c66affSColin Finck
206c2c66affSColin Finck /* Finally, adjust the recommended wait time downward
207c2c66affSColin Finck by the amount of time the processing routines
208c2c66affSColin Finck actually took */
209c2c66affSColin Finck adjust_time = GetTickCount() - cur_time;
210c2c66affSColin Finck if (adjust_time > ret_time)
211c2c66affSColin Finck ret_time = 0;
212c2c66affSColin Finck else
213c2c66affSColin Finck ret_time -= adjust_time;
214c2c66affSColin Finck
215c2c66affSColin Finck /* We return the amount of time our caller should sleep
216c2c66affSColin Finck before needing to check in on us again */
217c2c66affSColin Finck return(ret_time);
218c2c66affSColin Finck }
219c2c66affSColin Finck
220c2c66affSColin Finck /**************************************************************************
221c2c66affSColin Finck * TIME_MMSysTimeThread
222c2c66affSColin Finck */
TIME_MMSysTimeThread(LPVOID arg)223c2c66affSColin Finck static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
224c2c66affSColin Finck {
225c2c66affSColin Finck DWORD sleep_time;
226c2c66affSColin Finck DWORD rc;
227c2c66affSColin Finck
228c2c66affSColin Finck TRACE("Starting main winmm thread\n");
229c2c66affSColin Finck
230c2c66affSColin Finck /* FIXME: As an optimization, we could have
231c2c66affSColin Finck this thread die when there are no more requests
232c2c66affSColin Finck pending, and then get recreated on the first
233c2c66affSColin Finck new event; it's not clear if that would be worth
234c2c66affSColin Finck it or not. */
235c2c66affSColin Finck
236c2c66affSColin Finck while (! TIME_TimeToDie)
237c2c66affSColin Finck {
238c2c66affSColin Finck sleep_time = TIME_MMSysTimeCallback();
239c2c66affSColin Finck
240c2c66affSColin Finck if (sleep_time == 0)
241c2c66affSColin Finck continue;
242c2c66affSColin Finck
243c2c66affSColin Finck rc = WaitForSingleObject(TIME_hWakeEvent, sleep_time);
244c2c66affSColin Finck if (rc != WAIT_TIMEOUT && rc != WAIT_OBJECT_0)
245c2c66affSColin Finck {
246c2c66affSColin Finck FIXME("Unexpected error %ld(%ld) in timer thread\n", rc, GetLastError());
247c2c66affSColin Finck break;
248c2c66affSColin Finck }
249c2c66affSColin Finck }
250c2c66affSColin Finck TRACE("Exiting main winmm thread\n");
251c2c66affSColin Finck return 0;
252c2c66affSColin Finck }
253c2c66affSColin Finck
254c2c66affSColin Finck /**************************************************************************
255c2c66affSColin Finck * TIME_MMTimeStart
256c2c66affSColin Finck */
TIME_MMTimeStart(void)257c2c66affSColin Finck void TIME_MMTimeStart(void)
258c2c66affSColin Finck {
259c2c66affSColin Finck if (!TIME_hMMTimer) {
260c2c66affSColin Finck TIME_TimersList = NULL;
261c2c66affSColin Finck TIME_hWakeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
262c2c66affSColin Finck TIME_TimeToDie = FALSE;
263c2c66affSColin Finck TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, NULL, 0, NULL);
264c2c66affSColin Finck SetThreadPriority(TIME_hMMTimer, THREAD_PRIORITY_TIME_CRITICAL);
265c2c66affSColin Finck }
266c2c66affSColin Finck }
267c2c66affSColin Finck
268c2c66affSColin Finck /**************************************************************************
269c2c66affSColin Finck * TIME_MMTimeStop
270c2c66affSColin Finck */
TIME_MMTimeStop(void)271c2c66affSColin Finck void TIME_MMTimeStop(void)
272c2c66affSColin Finck {
273c2c66affSColin Finck if (TIME_hMMTimer) {
274c2c66affSColin Finck
275c2c66affSColin Finck TIME_TimeToDie = TRUE;
276c2c66affSColin Finck SetEvent(TIME_hWakeEvent);
277c2c66affSColin Finck
278c2c66affSColin Finck /* FIXME: in the worst case, we're going to wait 65 seconds here :-( */
279c2c66affSColin Finck WaitForSingleObject(TIME_hMMTimer, INFINITE);
280c2c66affSColin Finck
281c2c66affSColin Finck CloseHandle(TIME_hMMTimer);
282c2c66affSColin Finck CloseHandle(TIME_hWakeEvent);
283c2c66affSColin Finck TIME_hMMTimer = 0;
284c2c66affSColin Finck TIME_TimersList = NULL;
285c2c66affSColin Finck }
286c2c66affSColin Finck }
287c2c66affSColin Finck
288c2c66affSColin Finck /**************************************************************************
289c2c66affSColin Finck * timeGetSystemTime [WINMM.@]
290c2c66affSColin Finck */
timeGetSystemTime(LPMMTIME lpTime,UINT wSize)291c2c66affSColin Finck MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
292c2c66affSColin Finck {
293c2c66affSColin Finck
294c2c66affSColin Finck if (wSize >= sizeof(*lpTime)) {
295c2c66affSColin Finck lpTime->wType = TIME_MS;
296c2c66affSColin Finck lpTime->u.ms = GetTickCount();
297c2c66affSColin Finck
298c2c66affSColin Finck }
299c2c66affSColin Finck
300c2c66affSColin Finck return 0;
301c2c66affSColin Finck }
302c2c66affSColin Finck
303c2c66affSColin Finck /**************************************************************************
304c2c66affSColin Finck * TIME_SetEventInternal [internal]
305c2c66affSColin Finck */
TIME_SetEventInternal(UINT wDelay,UINT wResol,LPTIMECALLBACK lpFunc,DWORD dwUser,UINT wFlags)306c2c66affSColin Finck WORD TIME_SetEventInternal(UINT wDelay, UINT wResol,
307c2c66affSColin Finck LPTIMECALLBACK lpFunc, DWORD dwUser, UINT wFlags)
308c2c66affSColin Finck {
309c2c66affSColin Finck WORD wNewID = 0;
310c2c66affSColin Finck LPWINE_TIMERENTRY lpNewTimer;
311c2c66affSColin Finck LPWINE_TIMERENTRY lpTimer;
312c2c66affSColin Finck
313c2c66affSColin Finck TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
314c2c66affSColin Finck
315c2c66affSColin Finck if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)
316c2c66affSColin Finck return 0;
317c2c66affSColin Finck
318c2c66affSColin Finck lpNewTimer = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));
319c2c66affSColin Finck if (lpNewTimer == NULL)
320c2c66affSColin Finck return 0;
321c2c66affSColin Finck
322c2c66affSColin Finck TIME_MMTimeStart();
323c2c66affSColin Finck
324c2c66affSColin Finck lpNewTimer->wDelay = wDelay;
325c2c66affSColin Finck lpNewTimer->dwTriggerTime = GetTickCount() + wDelay;
326c2c66affSColin Finck
327c2c66affSColin Finck /* FIXME - wResol is not respected, although it is not clear
328c2c66affSColin Finck that we could change our precision meaningfully */
329c2c66affSColin Finck lpNewTimer->wResol = wResol;
330c2c66affSColin Finck lpNewTimer->lpFunc = lpFunc;
331c2c66affSColin Finck lpNewTimer->dwUser = dwUser;
332c2c66affSColin Finck lpNewTimer->wFlags = wFlags;
333c2c66affSColin Finck
334c2c66affSColin Finck EnterCriticalSection(&WINMM_cs);
335c2c66affSColin Finck
336c2c66affSColin Finck if ((wFlags & TIME_KILL_SYNCHRONOUS) && !TIME_hKillEvent)
337c2c66affSColin Finck TIME_hKillEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
338c2c66affSColin Finck
339c2c66affSColin Finck for (lpTimer = TIME_TimersList; lpTimer != NULL; lpTimer = lpTimer->lpNext) {
340c2c66affSColin Finck wNewID = max(wNewID, lpTimer->wTimerID);
341c2c66affSColin Finck }
342c2c66affSColin Finck
343c2c66affSColin Finck lpNewTimer->lpNext = TIME_TimersList;
344c2c66affSColin Finck TIME_TimersList = lpNewTimer;
345c2c66affSColin Finck lpNewTimer->wTimerID = wNewID + 1;
346c2c66affSColin Finck
347c2c66affSColin Finck LeaveCriticalSection(&WINMM_cs);
348c2c66affSColin Finck
349c2c66affSColin Finck /* Wake the service thread in case there is work to be done */
350c2c66affSColin Finck SetEvent(TIME_hWakeEvent);
351c2c66affSColin Finck
352c2c66affSColin Finck TRACE("=> %u\n", wNewID + 1);
353c2c66affSColin Finck
354c2c66affSColin Finck return wNewID + 1;
355c2c66affSColin Finck }
356c2c66affSColin Finck
357c2c66affSColin Finck /**************************************************************************
358c2c66affSColin Finck * timeSetEvent [WINMM.@]
359c2c66affSColin Finck */
timeSetEvent(UINT wDelay,UINT wResol,LPTIMECALLBACK lpFunc,DWORD_PTR dwUser,UINT wFlags)360c2c66affSColin Finck MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
361c2c66affSColin Finck DWORD_PTR dwUser, UINT wFlags)
362c2c66affSColin Finck {
363c2c66affSColin Finck return TIME_SetEventInternal(wDelay, wResol, lpFunc,
364c2c66affSColin Finck dwUser, wFlags);
365c2c66affSColin Finck }
366c2c66affSColin Finck
367c2c66affSColin Finck /**************************************************************************
368c2c66affSColin Finck * timeKillEvent [WINMM.@]
369c2c66affSColin Finck */
timeKillEvent(UINT wID)370c2c66affSColin Finck MMRESULT WINAPI timeKillEvent(UINT wID)
371c2c66affSColin Finck {
372c2c66affSColin Finck LPWINE_TIMERENTRY lpSelf = NULL, *lpTimer;
373c2c66affSColin Finck
374c2c66affSColin Finck TRACE("(%u)\n", wID);
375c2c66affSColin Finck EnterCriticalSection(&WINMM_cs);
376c2c66affSColin Finck /* remove WINE_TIMERENTRY from list */
377c2c66affSColin Finck for (lpTimer = &TIME_TimersList; *lpTimer; lpTimer = &(*lpTimer)->lpNext) {
378c2c66affSColin Finck if (wID == (*lpTimer)->wTimerID) {
379c2c66affSColin Finck lpSelf = *lpTimer;
380c2c66affSColin Finck /* unlink timer of id 'wID' */
381c2c66affSColin Finck *lpTimer = (*lpTimer)->lpNext;
382c2c66affSColin Finck break;
383c2c66affSColin Finck }
384c2c66affSColin Finck }
385c2c66affSColin Finck LeaveCriticalSection(&WINMM_cs);
386c2c66affSColin Finck
387c2c66affSColin Finck if (!lpSelf)
388c2c66affSColin Finck {
389c2c66affSColin Finck WARN("wID=%u is not a valid timer ID\n", wID);
390c2c66affSColin Finck return MMSYSERR_INVALPARAM;
391c2c66affSColin Finck }
392c2c66affSColin Finck if (lpSelf->wFlags & TIME_KILL_SYNCHRONOUS)
393c2c66affSColin Finck WaitForSingleObject(TIME_hKillEvent, INFINITE);
394c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, lpSelf);
395c2c66affSColin Finck return TIMERR_NOERROR;
396c2c66affSColin Finck }
397c2c66affSColin Finck
398c2c66affSColin Finck /**************************************************************************
399c2c66affSColin Finck * timeGetDevCaps [WINMM.@]
400c2c66affSColin Finck */
timeGetDevCaps(LPTIMECAPS lpCaps,UINT wSize)401c2c66affSColin Finck MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
402c2c66affSColin Finck {
403c2c66affSColin Finck TRACE("(%p, %u)\n", lpCaps, wSize);
404c2c66affSColin Finck
405c2c66affSColin Finck if (lpCaps == 0) {
406c2c66affSColin Finck WARN("invalid lpCaps\n");
407c2c66affSColin Finck return TIMERR_NOCANDO;
408c2c66affSColin Finck }
409c2c66affSColin Finck
410c2c66affSColin Finck if (wSize < sizeof(TIMECAPS)) {
411c2c66affSColin Finck WARN("invalid wSize\n");
412c2c66affSColin Finck return TIMERR_NOCANDO;
413c2c66affSColin Finck }
414c2c66affSColin Finck
415c2c66affSColin Finck lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
416c2c66affSColin Finck lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
417c2c66affSColin Finck return TIMERR_NOERROR;
418c2c66affSColin Finck }
419c2c66affSColin Finck
420c2c66affSColin Finck /**************************************************************************
421c2c66affSColin Finck * timeBeginPeriod [WINMM.@]
422c2c66affSColin Finck */
timeBeginPeriod(UINT wPeriod)423c2c66affSColin Finck MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
424c2c66affSColin Finck {
425c2c66affSColin Finck if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
426c2c66affSColin Finck return TIMERR_NOCANDO;
427c2c66affSColin Finck
428*88300ec3SThomas Brogan /* High resolution timer requested, use QPC */
429*88300ec3SThomas Brogan if (wPeriod <= 5 && TIME_qpcFreq.QuadPart == 0)
430*88300ec3SThomas Brogan {
431*88300ec3SThomas Brogan if (QueryPerformanceFrequency(&TIME_qpcFreq))
432*88300ec3SThomas Brogan TIME_qpcFreq.QuadPart /= 1000;
433*88300ec3SThomas Brogan }
434*88300ec3SThomas Brogan
435c2c66affSColin Finck if (wPeriod > MMSYSTIME_MININTERVAL)
436c2c66affSColin Finck {
437c2c66affSColin Finck WARN("Stub; we set our timer resolution at minimum\n");
438c2c66affSColin Finck }
439c2c66affSColin Finck
440c2c66affSColin Finck return 0;
441c2c66affSColin Finck }
442c2c66affSColin Finck
443c2c66affSColin Finck /**************************************************************************
444c2c66affSColin Finck * timeEndPeriod [WINMM.@]
445c2c66affSColin Finck */
timeEndPeriod(UINT wPeriod)446c2c66affSColin Finck MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
447c2c66affSColin Finck {
448c2c66affSColin Finck if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
449c2c66affSColin Finck return TIMERR_NOCANDO;
450c2c66affSColin Finck
451*88300ec3SThomas Brogan /* High resolution timer no longer requested, stop using QPC */
452*88300ec3SThomas Brogan if (wPeriod <= 5 && TIME_qpcFreq.QuadPart != 0)
453*88300ec3SThomas Brogan TIME_qpcFreq.QuadPart = 0;
454*88300ec3SThomas Brogan
455c2c66affSColin Finck if (wPeriod > MMSYSTIME_MININTERVAL)
456c2c66affSColin Finck {
457c2c66affSColin Finck WARN("Stub; we set our timer resolution at minimum\n");
458c2c66affSColin Finck }
459c2c66affSColin Finck return 0;
460c2c66affSColin Finck }
461c2c66affSColin Finck
462c2c66affSColin Finck /**************************************************************************
463c2c66affSColin Finck * timeGetTime [MMSYSTEM.607]
464c2c66affSColin Finck * timeGetTime [WINMM.@]
465c2c66affSColin Finck */
timeGetTime(void)466c2c66affSColin Finck DWORD WINAPI timeGetTime(void)
467c2c66affSColin Finck {
468*88300ec3SThomas Brogan LARGE_INTEGER perfCount;
469c2c66affSColin Finck #if defined(COMMENTOUTPRIORTODELETING)
470c2c66affSColin Finck DWORD count;
471c2c66affSColin Finck
472c2c66affSColin Finck /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
473c2c66affSColin Finck * that lets mciavi.drv run correctly
474c2c66affSColin Finck */
475c2c66affSColin Finck if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
476c2c66affSColin Finck if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
477c2c66affSColin Finck #endif
478*88300ec3SThomas Brogan /* Use QPC if a high-resolution timer was requested (<= 5ms) */
479*88300ec3SThomas Brogan if (TIME_qpcFreq.QuadPart != 0)
480*88300ec3SThomas Brogan {
481*88300ec3SThomas Brogan QueryPerformanceCounter(&perfCount);
482*88300ec3SThomas Brogan return (DWORD)(perfCount.QuadPart / TIME_qpcFreq.QuadPart);
483*88300ec3SThomas Brogan }
484*88300ec3SThomas Brogan /* Otherwise continue using GetTickCount */
485c2c66affSColin Finck return GetTickCount();
486c2c66affSColin Finck }
487