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