📄 time.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
* MMSYSTEM time functions
*
* Copyright 1993 Martin Ayotte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "wine/port.h"
#include <stdarg.h>
#include <time.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "windef.h"
#include "winbase.h"
#include "mmsystem.h"
#include "winemm.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
static HANDLE TIME_hMMTimer;
static LPWINE_TIMERENTRY TIME_TimersList;
static HANDLE TIME_hKillEvent;
static HANDLE TIME_hWakeEvent;
static BOOL TIME_TimeToDie = TRUE;
/*
* Some observations on the behavior of winmm on Windows.
* First, the call to timeBeginPeriod(xx) can never be used
* to raise the timer resolution, only lower it.
*
* Second, a brief survey of a variety of Win 2k and Win X
* machines showed that a 'standard' (aka default) timer
* resolution was 1 ms (Win9x is documented as being 1). However, one
* machine had a standard timer resolution of 10 ms.
*
* Further, if we set our default resolution to 1,
* the implementation of timeGetTime becomes GetTickCount(),
* and we can optimize the code to reduce overhead.
*
* Additionally, a survey of Event behaviors shows that
* if we request a Periodic event every 50 ms, then Windows
* makes sure to trigger that event 20 times in the next
* second. If delays prevent that from happening on exact
* schedule, Windows will trigger the events as close
* to the original schedule as is possible, and will eventually
* bring the event triggers back onto a schedule that is
* consistent with what would have happened if there were
* no delays.
*
* Jeremy White, October 2004
*/
#define MMSYSTIME_MININTERVAL (1)
#define MMSYSTIME_MAXINTERVAL (65535)
static void TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
{
TRACE("%04lx:CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX dwTriggerTime %ld(delta %ld)\n",
GetCurrentThreadId(), lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser,
lpTimer->dwTriggerTime, GetTickCount() - lpTimer->dwTriggerTime);
/* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called
* during interrupt time, is allowed to execute very limited number of API calls (like
* PostMessage), and must reside in DLL (therefore uses stack of active application). So I
* guess current implementation via SetTimer has to be improved upon.
*/
switch (lpTimer->wFlags & 0x30) {
case TIME_CALLBACK_FUNCTION:
if (lpTimer->wFlags & WINE_TIMER_IS32)
(lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
else if (pFnCallMMDrvFunc16)
pFnCallMMDrvFunc16((DWORD)lpTimer->lpFunc, lpTimer->wTimerID, 0,
lpTimer->dwUser, 0, 0);
break;
case TIME_CALLBACK_EVENT_SET:
SetEvent((HANDLE)lpTimer->lpFunc);
break;
case TIME_CALLBACK_EVENT_PULSE:
PulseEvent((HANDLE)lpTimer->lpFunc);
break;
default:
FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n",
lpTimer->wFlags, lpTimer->lpFunc);
break;
}
}
/**************************************************************************
* TIME_MMSysTimeCallback
*/
static DWORD CALLBACK TIME_MMSysTimeCallback(LPWINE_MM_IDATA iData)
{
static int nSizeLpTimers;
static LPWINE_TIMERENTRY lpTimers;
LPWINE_TIMERENTRY timer, *ptimer, *next_ptimer;
int idx;
DWORD cur_time;
DWORD delta_time;
DWORD ret_time = INFINITE;
DWORD adjust_time;
/* optimize for the most frequent case - no events */
if (! TIME_TimersList)
return(ret_time);
/* since timeSetEvent() and timeKillEvent() can be called
* from 16 bit code, there are cases where win16 lock is
* locked upon entering timeSetEvent(), and then the mm timer
* critical section is locked. This function cannot call the
* timer callback with the crit sect locked (because callback
* may need to acquire Win16 lock, thus providing a deadlock
* situation).
* To cope with that, we just copy the WINE_TIMERENTRY struct
* that need to trigger the callback, and call it without the
* mm timer crit sect locked.
* the hKillTimeEvent is used to mark the section where we
* handle the callbacks so we can do synchronous kills.
* EPP 99/07/13, updated 04/01/10
*/
idx = 0;
cur_time = GetTickCount();
EnterCriticalSection(&iData->cs);
for (ptimer = &TIME_TimersList; *ptimer != NULL; ) {
timer = *ptimer;
next_ptimer = &timer->lpNext;
if (cur_time >= timer->dwTriggerTime)
{
if (timer->lpFunc) {
if (idx == nSizeLpTimers) {
if (lpTimers)
lpTimers = (LPWINE_TIMERENTRY)
HeapReAlloc(GetProcessHeap(), 0, lpTimers,
++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
else
lpTimers = (LPWINE_TIMERENTRY)
HeapAlloc(GetProcessHeap(), 0,
++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
}
lpTimers[idx++] = *timer;
}
/* Update the time after we make the copy to preserve
the original trigger time */
timer->dwTriggerTime += timer->wDelay;
/* TIME_ONESHOT is defined as 0 */
if (!(timer->wFlags & TIME_PERIODIC))
{
/* unlink timer from timers list */
*ptimer = *next_ptimer;
HeapFree(GetProcessHeap(), 0, timer);
/* We don't need to trigger oneshots again */
delta_time = INFINITE;
}
else
{
/* Compute when this event needs this function
to be called again */
if (timer->dwTriggerTime <= cur_time)
delta_time = 0;
else
delta_time = timer->dwTriggerTime - cur_time;
}
}
else
delta_time = timer->dwTriggerTime - cur_time;
/* Determine when we need to return to this function */
ret_time = min(ret_time, delta_time);
ptimer = next_ptimer;
}
if (TIME_hKillEvent) ResetEvent(TIME_hKillEvent);
LeaveCriticalSection(&iData->cs);
while (idx > 0) TIME_TriggerCallBack(&lpTimers[--idx]);
if (TIME_hKillEvent) SetEvent(TIME_hKillEvent);
/* Finally, adjust the recommended wait time downward
by the amount of time the processing routines
actually took */
adjust_time = GetTickCount() - cur_time;
if (adjust_time > ret_time)
ret_time = 0;
else
ret_time -= adjust_time;
/* We return the amount of time our caller should sleep
before needing to check in on us again */
return(ret_time);
}
/**************************************************************************
* TIME_MMSysTimeThread
*/
static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
{
LPWINE_MM_IDATA iData = (LPWINE_MM_IDATA)arg;
DWORD sleep_time;
DWORD rc;
TRACE("Starting main winmm thread\n");
/* FIXME: As an optimization, we could have
this thread die when there are no more requests
pending, and then get recreated on the first
new event; it's not clear if that would be worth
it or not. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -