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