ntdll: Buffer pagemap reads in fill_working_set_info().
[wine.git] / dlls / winmm / time.c
blobe00b5ca7b54b52d7d43ff6e5d23385f46d0a0d3e
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 /*
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <stdarg.h>
24 #include <errno.h>
25 #include <time.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "mmsystem.h"
31 #include "winemm.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
37 typedef struct tagWINE_TIMERENTRY {
38 UINT wDelay;
39 UINT wResol;
40 LPTIMECALLBACK lpFunc; /* can be lots of things */
41 DWORD_PTR dwUser;
42 UINT16 wFlags;
43 UINT16 wTimerID;
44 DWORD dwTriggerTime;
45 } WINE_TIMERENTRY, *LPWINE_TIMERENTRY;
47 static WINE_TIMERENTRY timers[16];
48 static UINT timers_created;
50 static CRITICAL_SECTION TIME_cbcrst;
51 static CRITICAL_SECTION_DEBUG critsect_debug =
53 0, 0, &TIME_cbcrst,
54 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
55 0, 0, { (DWORD_PTR)(__FILE__ ": TIME_cbcrst") }
57 static CRITICAL_SECTION TIME_cbcrst = { &critsect_debug, -1, 0, 0, 0, 0 };
59 static HANDLE TIME_hMMTimer;
60 static CONDITION_VARIABLE TIME_cv;
63 * Some observations on the behavior of winmm on Windows.
65 * First, the call to timeBeginPeriod(xx) can never be used to
66 * lower the timer resolution (i.e. increase the update
67 * interval), only to increase the timer resolution (i.e. lower
68 * the update interval).
70 * Second, a brief survey of a variety of Win 2k and Win X
71 * machines showed that a 'standard' (aka default) timer
72 * resolution was 1 ms (Win9x is documented as being 1). However, one
73 * machine had a standard timer resolution of 10 ms.
75 * Further, timeBeginPeriod(xx) also affects the resolution of
76 * wait calls such as NtDelayExecution() and
77 * NtWaitForMultipleObjects() which by default round up their
78 * timeout to the nearest multiple of 15.625ms across all Windows
79 * versions. In Wine all of those currently work with sub-1ms
80 * accuracy.
82 * Effective time resolution is a global value that is the max
83 * of the resolutions (i.e. min of update intervals) requested by
84 * all the processes. A lot of programs seem to do
85 * timeBeginPeriod(1) forcing it onto everyone else.
87 * Defaulting to 1ms accuracy in winmm should be safe.
89 * Additionally, a survey of Event behaviors shows that
90 * if we request a Periodic event every 50 ms, then Windows
91 * makes sure to trigger that event 20 times in the next
92 * second. If delays prevent that from happening on exact
93 * schedule, Windows will trigger the events as close
94 * to the original schedule as is possible, and will eventually
95 * bring the event triggers back onto a schedule that is
96 * consistent with what would have happened if there were
97 * no delays.
99 * Jeremy White, October 2004
100 * Arkadiusz Hiler, August 2020
102 #define MMSYSTIME_MININTERVAL (1)
103 #define MMSYSTIME_MAXINTERVAL (65535)
105 /**************************************************************************
106 * TIME_MMSysTimeCallback
108 static int TIME_MMSysTimeCallback(void)
110 WINE_TIMERENTRY *timer;
111 int i, delta_time;
113 /* since timeSetEvent() and timeKillEvent() can be called
114 * from 16 bit code, there are cases where win16 lock is
115 * locked upon entering timeSetEvent(), and then the mm timer
116 * critical section is locked. This function cannot call the
117 * timer callback with the crit sect locked (because callback
118 * may need to acquire Win16 lock, thus providing a deadlock
119 * situation).
120 * To cope with that, we just copy the WINE_TIMERENTRY struct
121 * that need to trigger the callback, and call it without the
122 * mm timer crit sect locked.
125 for (;;)
127 for (i = 0; i < ARRAY_SIZE(timers); i++)
128 if (timers[i].wTimerID) break;
129 if (i == ARRAY_SIZE(timers)) return -1;
130 timer = timers + i;
131 for (i++; i < ARRAY_SIZE(timers); i++)
133 if (!timers[i].wTimerID) continue;
134 if (timers[i].dwTriggerTime < timer->dwTriggerTime)
135 timer = timers + i;
138 delta_time = timer->dwTriggerTime - timeGetTime();
139 if (delta_time > 0) break;
141 if (timer->wFlags & TIME_PERIODIC)
142 timer->dwTriggerTime += timer->wDelay;
144 switch(timer->wFlags & (TIME_CALLBACK_EVENT_SET|TIME_CALLBACK_EVENT_PULSE))
146 case TIME_CALLBACK_EVENT_SET:
147 SetEvent(timer->lpFunc);
148 break;
149 case TIME_CALLBACK_EVENT_PULSE:
150 PulseEvent(timer->lpFunc);
151 break;
152 case TIME_CALLBACK_FUNCTION:
154 DWORD_PTR user = timer->dwUser;
155 UINT16 id = timer->wTimerID;
156 UINT16 flags = timer->wFlags;
157 LPTIMECALLBACK func = timer->lpFunc;
159 if (flags & TIME_KILL_SYNCHRONOUS) EnterCriticalSection(&TIME_cbcrst);
160 LeaveCriticalSection(&WINMM_cs);
162 func(id, 0, user, 0, 0);
164 EnterCriticalSection(&WINMM_cs);
165 if (flags & TIME_KILL_SYNCHRONOUS) LeaveCriticalSection(&TIME_cbcrst);
166 if (id != timer->wTimerID) timer = NULL;
168 break;
170 if (timer && !(timer->wFlags & TIME_PERIODIC))
171 timer->wTimerID = 0;
173 return delta_time;
176 /**************************************************************************
177 * TIME_MMSysTimeThread
179 static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
181 int sleep_time;
182 BOOL ret;
184 TRACE("Starting main winmm thread\n");
186 EnterCriticalSection(&WINMM_cs);
187 while (1)
189 sleep_time = TIME_MMSysTimeCallback();
191 if (sleep_time < 0)
192 break;
193 if (sleep_time == 0)
194 continue;
196 ret = SleepConditionVariableCS(&TIME_cv, &WINMM_cs, sleep_time);
197 if (!ret && GetLastError() != ERROR_TIMEOUT)
199 ERR("Unexpected error in poll: %s(%d)\n", strerror(errno), errno);
200 break;
203 CloseHandle(TIME_hMMTimer);
204 TIME_hMMTimer = NULL;
205 LeaveCriticalSection(&WINMM_cs);
206 TRACE("Exiting main winmm thread\n");
207 FreeLibraryAndExitThread(arg, 0);
208 return 0;
211 /**************************************************************************
212 * TIME_MMTimeStart
214 static void TIME_MMTimeStart(void)
216 HMODULE mod;
217 if (TIME_hMMTimer) return;
219 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)TIME_MMSysTimeThread, &mod);
220 TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, mod, 0, NULL);
221 SetThreadPriority(TIME_hMMTimer, THREAD_PRIORITY_TIME_CRITICAL);
224 /**************************************************************************
225 * TIME_MMTimeStop
227 void TIME_MMTimeStop(void)
229 if (TIME_hMMTimer) {
230 EnterCriticalSection(&WINMM_cs);
231 if (TIME_hMMTimer) {
232 ERR("Timer still active?!\n");
233 CloseHandle(TIME_hMMTimer);
235 DeleteCriticalSection(&TIME_cbcrst);
239 /**************************************************************************
240 * timeGetSystemTime [WINMM.@]
242 MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
244 if (wSize >= sizeof(*lpTime)) {
245 lpTime->wType = TIME_MS;
246 lpTime->u.ms = timeGetTime();
249 return 0;
252 /**************************************************************************
253 * timeGetTime [WINMM.@]
255 DWORD WINAPI timeGetTime(void)
257 LARGE_INTEGER now, freq;
259 QueryPerformanceCounter(&now);
260 QueryPerformanceFrequency(&freq);
262 return (now.QuadPart * 1000) / freq.QuadPart;
265 /**************************************************************************
266 * timeSetEvent [WINMM.@]
268 MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
269 DWORD_PTR dwUser, UINT wFlags)
271 WORD new_id = 0;
272 int i;
274 TRACE("(%u, %u, %p, %08IX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
276 if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)
277 return 0;
279 EnterCriticalSection(&WINMM_cs);
281 for (i = 0; i < ARRAY_SIZE(timers); i++)
282 if (!timers[i].wTimerID) break;
283 if (i == ARRAY_SIZE(timers))
285 LeaveCriticalSection(&WINMM_cs);
286 return 0;
289 new_id = ARRAY_SIZE(timers)*(++timers_created) + i;
290 if (!new_id) new_id = ARRAY_SIZE(timers)*(++timers_created) + i;
292 timers[i].wDelay = wDelay;
293 timers[i].dwTriggerTime = timeGetTime() + wDelay;
295 /* FIXME - wResol is not respected, although it is not clear
296 that we could change our precision meaningfully */
297 timers[i].wResol = wResol;
298 timers[i].lpFunc = lpFunc;
299 timers[i].dwUser = dwUser;
300 timers[i].wFlags = wFlags;
301 timers[i].wTimerID = new_id;
303 TIME_MMTimeStart();
305 LeaveCriticalSection(&WINMM_cs);
307 /* Wake the service thread in case there is work to be done */
308 WakeConditionVariable(&TIME_cv);
310 TRACE("=> %u\n", new_id);
312 return new_id;
315 /**************************************************************************
316 * timeKillEvent [WINMM.@]
318 MMRESULT WINAPI timeKillEvent(UINT wID)
320 WINE_TIMERENTRY *timer;
321 WORD flags;
323 TRACE("(%u)\n", wID);
324 EnterCriticalSection(&WINMM_cs);
326 timer = &timers[wID % ARRAY_SIZE(timers)];
327 if (timer->wTimerID != wID)
329 LeaveCriticalSection(&WINMM_cs);
330 WARN("wID=%u is not a valid timer ID\n", wID);
331 return TIMERR_NOCANDO;
334 timer->wTimerID = 0;
335 flags = timer->wFlags;
336 LeaveCriticalSection(&WINMM_cs);
338 if (flags & TIME_KILL_SYNCHRONOUS)
340 EnterCriticalSection(&TIME_cbcrst);
341 LeaveCriticalSection(&TIME_cbcrst);
343 WakeConditionVariable(&TIME_cv);
344 return TIMERR_NOERROR;
347 /**************************************************************************
348 * timeGetDevCaps [WINMM.@]
350 MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
352 TRACE("(%p, %u)\n", lpCaps, wSize);
354 if (lpCaps == 0) {
355 WARN("invalid lpCaps\n");
356 return TIMERR_NOCANDO;
359 if (wSize < sizeof(TIMECAPS)) {
360 WARN("invalid wSize\n");
361 return TIMERR_NOCANDO;
364 lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
365 lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
366 return TIMERR_NOERROR;
369 /**************************************************************************
370 * timeBeginPeriod [WINMM.@]
372 MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
374 if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
375 return TIMERR_NOCANDO;
377 if (wPeriod > MMSYSTIME_MININTERVAL)
379 WARN("Stub; we set our timer resolution at minimum\n");
382 return 0;
385 /**************************************************************************
386 * timeEndPeriod [WINMM.@]
388 MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
390 if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
391 return TIMERR_NOCANDO;
393 if (wPeriod > MMSYSTIME_MININTERVAL)
395 WARN("Stub; we set our timer resolution at minimum\n");
397 return 0;