⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 2 页
字号:
/* -*- 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 + -