ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

time.c
Go to the documentation of this file.
00001 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
00002 
00003 /*
00004  * MMSYSTEM time functions
00005  *
00006  * Copyright 1993 Martin Ayotte
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00023 #include "config.h"
00024 #include "wine/port.h"
00025 
00026 #include <stdarg.h>
00027 #include <time.h>
00028 #ifdef HAVE_SYS_TIME_H
00029 # include <sys/time.h>
00030 #endif
00031 #ifdef HAVE_UNISTD_H
00032 # include <unistd.h>
00033 #endif
00034 
00035 #include "windef.h"
00036 #include "winbase.h"
00037 #include "mmsystem.h"
00038 
00039 #include "winemm.h"
00040 
00041 #include "wine/debug.h"
00042 
00043 WINE_DEFAULT_DEBUG_CHANNEL(mmtime);
00044 
00045 typedef struct tagWINE_TIMERENTRY {
00046     UINT            wDelay;
00047     UINT            wResol;
00048     LPTIMECALLBACK              lpFunc; /* can be lots of things */
00049     DWORD           dwUser;
00050     UINT16          wFlags;
00051     UINT16          wTimerID;
00052     DWORD           dwTriggerTime;
00053     struct tagWINE_TIMERENTRY*  lpNext;
00054 } WINE_TIMERENTRY, *LPWINE_TIMERENTRY;
00055 
00056 static    HANDLE                TIME_hMMTimer;
00057 static    LPWINE_TIMERENTRY     TIME_TimersList;
00058 static    HANDLE                TIME_hKillEvent;
00059 static    HANDLE                TIME_hWakeEvent;
00060 static    BOOL                  TIME_TimeToDie = TRUE;
00061 
00062 /*
00063  * Some observations on the behavior of winmm on Windows.
00064  * First, the call to timeBeginPeriod(xx) can never be used
00065  * to raise the timer resolution, only lower it.
00066  *
00067  * Second, a brief survey of a variety of Win 2k and Win X
00068  * machines showed that a 'standard' (aka default) timer
00069  * resolution was 1 ms (Win9x is documented as being 1).  However, one
00070  * machine had a standard timer resolution of 10 ms.
00071  *
00072  * Further, if we set our default resolution to 1,
00073  * the implementation of timeGetTime becomes GetTickCount(),
00074  * and we can optimize the code to reduce overhead.
00075  *
00076  * Additionally, a survey of Event behaviors shows that
00077  * if we request a Periodic event every 50 ms, then Windows
00078  * makes sure to trigger that event 20 times in the next
00079  * second.  If delays prevent that from happening on exact
00080  * schedule, Windows will trigger the events as close
00081  * to the original schedule as is possible, and will eventually
00082  * bring the event triggers back onto a schedule that is
00083  * consistent with what would have happened if there were
00084  * no delays.
00085  *
00086  *   Jeremy White, October 2004
00087  */
00088 #define MMSYSTIME_MININTERVAL (1)
00089 #define MMSYSTIME_MAXINTERVAL (65535)
00090 
00091 
00092 static  void    TIME_TriggerCallBack(LPWINE_TIMERENTRY lpTimer)
00093 {
00094     TRACE("%04lx:CallBack => lpFunc=%p wTimerID=%04X dwUser=%08lX dwTriggerTime %ld(delta %ld)\n",
00095       GetCurrentThreadId(), lpTimer->lpFunc, lpTimer->wTimerID, lpTimer->dwUser,
00096           lpTimer->dwTriggerTime, GetTickCount() - lpTimer->dwTriggerTime);
00097 
00098     /* - TimeProc callback that is called here is something strange, under Windows 3.1x it is called
00099      *      during interrupt time,  is allowed to execute very limited number of API calls (like
00100      *          PostMessage), and must reside in DLL (therefore uses stack of active application). So I
00101      *       guess current implementation via SetTimer has to be improved upon.
00102      */
00103     switch (lpTimer->wFlags & 0x30) {
00104     case TIME_CALLBACK_FUNCTION:
00105         (lpTimer->lpFunc)(lpTimer->wTimerID, 0, lpTimer->dwUser, 0, 0);
00106     break;
00107     case TIME_CALLBACK_EVENT_SET:
00108     SetEvent((HANDLE)lpTimer->lpFunc);
00109     break;
00110     case TIME_CALLBACK_EVENT_PULSE:
00111     PulseEvent((HANDLE)lpTimer->lpFunc);
00112     break;
00113     default:
00114     FIXME("Unknown callback type 0x%04x for mmtime callback (%p), ignored.\n",
00115           lpTimer->wFlags, lpTimer->lpFunc);
00116     break;
00117     }
00118 }
00119 
00120 /**************************************************************************
00121  *           TIME_MMSysTimeCallback
00122  */
00123 static DWORD CALLBACK TIME_MMSysTimeCallback()
00124 {
00125 static    int               nSizeLpTimers;
00126 static    LPWINE_TIMERENTRY     lpTimers;
00127 
00128     LPWINE_TIMERENTRY   timer, *ptimer, *next_ptimer;
00129     int         idx;
00130     DWORD               cur_time;
00131     DWORD               delta_time;
00132     DWORD               ret_time = INFINITE;
00133     DWORD               adjust_time;
00134 
00135 
00136     /* optimize for the most frequent case  - no events */
00137     if (! TIME_TimersList)
00138         return(ret_time);
00139 
00140     /* since timeSetEvent() and timeKillEvent() can be called
00141      * from 16 bit code, there are cases where win16 lock is
00142      * locked upon entering timeSetEvent(), and then the mm timer
00143      * critical section is locked. This function cannot call the
00144      * timer callback with the crit sect locked (because callback
00145      * may need to acquire Win16 lock, thus providing a deadlock
00146      * situation).
00147      * To cope with that, we just copy the WINE_TIMERENTRY struct
00148      * that need to trigger the callback, and call it without the
00149      * mm timer crit sect locked.
00150      * the hKillTimeEvent is used to mark the section where we
00151      * handle the callbacks so we can do synchronous kills.
00152      * EPP 99/07/13, updated 04/01/10
00153      */
00154     idx = 0;
00155     cur_time = GetTickCount();
00156 
00157     EnterCriticalSection(&WINMM_cs);
00158     for (ptimer = &TIME_TimersList; *ptimer != NULL; ) {
00159         timer = *ptimer;
00160         next_ptimer = &timer->lpNext;
00161         if (cur_time >= timer->dwTriggerTime)
00162         {
00163             if (timer->lpFunc) {
00164                 if (idx == nSizeLpTimers) {
00165                     if (lpTimers)
00166                         lpTimers = (LPWINE_TIMERENTRY)
00167                             HeapReAlloc(GetProcessHeap(), 0, lpTimers,
00168                                         ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
00169                     else
00170                         lpTimers = (LPWINE_TIMERENTRY)
00171                         HeapAlloc(GetProcessHeap(), 0,
00172                                   ++nSizeLpTimers * sizeof(WINE_TIMERENTRY));
00173                 }
00174                 lpTimers[idx++] = *timer;
00175 
00176             }
00177 
00178             /* Update the time after we make the copy to preserve
00179                the original trigger time    */
00180             timer->dwTriggerTime += timer->wDelay;
00181 
00182             /* TIME_ONESHOT is defined as 0 */
00183             if (!(timer->wFlags & TIME_PERIODIC))
00184             {
00185                 /* unlink timer from timers list */
00186                 *ptimer = *next_ptimer;
00187                 HeapFree(GetProcessHeap(), 0, timer);
00188 
00189                 /* We don't need to trigger oneshots again */
00190                 delta_time = INFINITE;
00191             }
00192             else
00193             {
00194                 /* Compute when this event needs this function
00195                     to be called again */
00196                 if (timer->dwTriggerTime <= cur_time)
00197                     delta_time = 0;
00198                 else
00199                     delta_time = timer->dwTriggerTime - cur_time;
00200             }
00201         }
00202         else
00203             delta_time = timer->dwTriggerTime - cur_time;
00204 
00205         /* Determine when we need to return to this function */
00206         ret_time = min(ret_time, delta_time);
00207 
00208         ptimer = next_ptimer;
00209     }
00210     if (TIME_hKillEvent) ResetEvent(TIME_hKillEvent);
00211     LeaveCriticalSection(&WINMM_cs);
00212 
00213     while (idx > 0) TIME_TriggerCallBack(&lpTimers[--idx]);
00214     if (TIME_hKillEvent) SetEvent(TIME_hKillEvent);
00215 
00216     /* Finally, adjust the recommended wait time downward
00217        by the amount of time the processing routines
00218        actually took */
00219     adjust_time = GetTickCount() - cur_time;
00220     if (adjust_time > ret_time)
00221         ret_time = 0;
00222     else
00223         ret_time -= adjust_time;
00224 
00225     /* We return the amount of time our caller should sleep
00226        before needing to check in on us again       */
00227     return(ret_time);
00228 }
00229 
00230 /**************************************************************************
00231  *           TIME_MMSysTimeThread
00232  */
00233 static DWORD CALLBACK TIME_MMSysTimeThread(LPVOID arg)
00234 {
00235     DWORD sleep_time;
00236     DWORD rc;
00237 
00238     TRACE("Starting main winmm thread\n");
00239 
00240     /* FIXME:  As an optimization, we could have
00241                this thread die when there are no more requests
00242                pending, and then get recreated on the first
00243                new event; it's not clear if that would be worth
00244                it or not.                 */
00245 
00246     while (! TIME_TimeToDie)
00247     {
00248     sleep_time = TIME_MMSysTimeCallback();
00249 
00250         if (sleep_time == 0)
00251             continue;
00252 
00253         rc = WaitForSingleObject(TIME_hWakeEvent, sleep_time);
00254         if (rc != WAIT_TIMEOUT && rc != WAIT_OBJECT_0)
00255         {
00256             FIXME("Unexpected error %ld(%ld) in timer thread\n", rc, GetLastError());
00257             break;
00258         }
00259     }
00260     TRACE("Exiting main winmm thread\n");
00261     return 0;
00262 }
00263 
00264 /**************************************************************************
00265  *              TIME_MMTimeStart
00266  */
00267 void    TIME_MMTimeStart(void)
00268 {
00269     if (!TIME_hMMTimer) {
00270     TIME_TimersList = NULL;
00271         TIME_hWakeEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
00272         TIME_TimeToDie = FALSE;
00273     TIME_hMMTimer = CreateThread(NULL, 0, TIME_MMSysTimeThread, NULL, 0, NULL);
00274         SetThreadPriority(TIME_hMMTimer, THREAD_PRIORITY_TIME_CRITICAL);
00275     }
00276 }
00277 
00278 /**************************************************************************
00279  *              TIME_MMTimeStop
00280  */
00281 void    TIME_MMTimeStop(void)
00282 {
00283     if (TIME_hMMTimer) {
00284 
00285         TIME_TimeToDie = TRUE;
00286         SetEvent(TIME_hWakeEvent);
00287 
00288         /* FIXME: in the worst case, we're going to wait 65 seconds here :-( */
00289     WaitForSingleObject(TIME_hMMTimer, INFINITE);
00290 
00291     CloseHandle(TIME_hMMTimer);
00292     CloseHandle(TIME_hWakeEvent);
00293     TIME_hMMTimer = 0;
00294         TIME_TimersList = NULL;
00295     }
00296 }
00297 
00298 /**************************************************************************
00299  *              timeGetSystemTime   [WINMM.@]
00300  */
00301 MMRESULT WINAPI timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
00302 {
00303 
00304     if (wSize >= sizeof(*lpTime)) {
00305     lpTime->wType = TIME_MS;
00306     lpTime->u.ms = GetTickCount();
00307 
00308     }
00309 
00310     return 0;
00311 }
00312 
00313 /**************************************************************************
00314  *              TIME_SetEventInternal   [internal]
00315  */
00316 WORD    TIME_SetEventInternal(UINT wDelay, UINT wResol,
00317                               LPTIMECALLBACK lpFunc, DWORD dwUser, UINT wFlags)
00318 {
00319     WORD        wNewID = 0;
00320     LPWINE_TIMERENTRY   lpNewTimer;
00321     LPWINE_TIMERENTRY   lpTimer;
00322 
00323     TRACE("(%u, %u, %p, %08lX, %04X);\n", wDelay, wResol, lpFunc, dwUser, wFlags);
00324 
00325     if (wDelay < MMSYSTIME_MININTERVAL || wDelay > MMSYSTIME_MAXINTERVAL)
00326     return 0;
00327 
00328     lpNewTimer = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_TIMERENTRY));
00329     if (lpNewTimer == NULL)
00330     return 0;
00331 
00332     TIME_MMTimeStart();
00333 
00334     lpNewTimer->wDelay = wDelay;
00335     lpNewTimer->dwTriggerTime = GetTickCount() + wDelay;
00336 
00337     /* FIXME - wResol is not respected, although it is not clear
00338                that we could change our precision meaningfully  */
00339     lpNewTimer->wResol = wResol;
00340     lpNewTimer->lpFunc = lpFunc;
00341     lpNewTimer->dwUser = dwUser;
00342     lpNewTimer->wFlags = wFlags;
00343 
00344     EnterCriticalSection(&WINMM_cs);
00345 
00346     if ((wFlags & TIME_KILL_SYNCHRONOUS) && !TIME_hKillEvent)
00347         TIME_hKillEvent = CreateEventW(NULL, TRUE, TRUE, NULL);
00348 
00349     for (lpTimer = TIME_TimersList; lpTimer != NULL; lpTimer = lpTimer->lpNext) {
00350     wNewID = max(wNewID, lpTimer->wTimerID);
00351     }
00352 
00353     lpNewTimer->lpNext = TIME_TimersList;
00354     TIME_TimersList = lpNewTimer;
00355     lpNewTimer->wTimerID = wNewID + 1;
00356 
00357     LeaveCriticalSection(&WINMM_cs);
00358 
00359     /* Wake the service thread in case there is work to be done */
00360     SetEvent(TIME_hWakeEvent);
00361 
00362     TRACE("=> %u\n", wNewID + 1);
00363 
00364     return wNewID + 1;
00365 }
00366 
00367 /**************************************************************************
00368  *              timeSetEvent        [WINMM.@]
00369  */
00370 MMRESULT WINAPI timeSetEvent(UINT wDelay, UINT wResol, LPTIMECALLBACK lpFunc,
00371                             DWORD_PTR dwUser, UINT wFlags)
00372 {
00373     return TIME_SetEventInternal(wDelay, wResol, lpFunc,
00374                                  dwUser, wFlags);
00375 }
00376 
00377 /**************************************************************************
00378  *              timeKillEvent       [WINMM.@]
00379  */
00380 MMRESULT WINAPI timeKillEvent(UINT wID)
00381 {
00382     LPWINE_TIMERENTRY   lpSelf = NULL, *lpTimer;
00383 
00384     TRACE("(%u)\n", wID);
00385     EnterCriticalSection(&WINMM_cs);
00386     /* remove WINE_TIMERENTRY from list */
00387     for (lpTimer = &TIME_TimersList; *lpTimer; lpTimer = &(*lpTimer)->lpNext) {
00388     if (wID == (*lpTimer)->wTimerID) {
00389             lpSelf = *lpTimer;
00390             /* unlink timer of id 'wID' */
00391             *lpTimer = (*lpTimer)->lpNext;
00392         break;
00393     }
00394     }
00395     LeaveCriticalSection(&WINMM_cs);
00396 
00397     if (!lpSelf)
00398     {
00399         WARN("wID=%u is not a valid timer ID\n", wID);
00400         return MMSYSERR_INVALPARAM;
00401     }
00402     if (lpSelf->wFlags & TIME_KILL_SYNCHRONOUS)
00403         WaitForSingleObject(TIME_hKillEvent, INFINITE);
00404     HeapFree(GetProcessHeap(), 0, lpSelf);
00405     return TIMERR_NOERROR;
00406 }
00407 
00408 /**************************************************************************
00409  *              timeGetDevCaps      [WINMM.@]
00410  */
00411 MMRESULT WINAPI timeGetDevCaps(LPTIMECAPS lpCaps, UINT wSize)
00412 {
00413     TRACE("(%p, %u)\n", lpCaps, wSize);
00414 
00415     if (lpCaps == 0) {
00416         WARN("invalid lpCaps\n");
00417         return TIMERR_NOCANDO;
00418     }
00419 
00420     if (wSize < sizeof(TIMECAPS)) {
00421         WARN("invalid wSize\n");
00422         return TIMERR_NOCANDO;
00423     }
00424 
00425     lpCaps->wPeriodMin = MMSYSTIME_MININTERVAL;
00426     lpCaps->wPeriodMax = MMSYSTIME_MAXINTERVAL;
00427     return TIMERR_NOERROR;
00428 }
00429 
00430 /**************************************************************************
00431  *              timeBeginPeriod     [WINMM.@]
00432  */
00433 MMRESULT WINAPI timeBeginPeriod(UINT wPeriod)
00434 {
00435     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
00436     return TIMERR_NOCANDO;
00437 
00438     if (wPeriod > MMSYSTIME_MININTERVAL)
00439     {
00440         WARN("Stub; we set our timer resolution at minimum\n");
00441     }
00442 
00443     return 0;
00444 }
00445 
00446 /**************************************************************************
00447  *              timeEndPeriod       [WINMM.@]
00448  */
00449 MMRESULT WINAPI timeEndPeriod(UINT wPeriod)
00450 {
00451     if (wPeriod < MMSYSTIME_MININTERVAL || wPeriod > MMSYSTIME_MAXINTERVAL)
00452     return TIMERR_NOCANDO;
00453 
00454     if (wPeriod > MMSYSTIME_MININTERVAL)
00455     {
00456         WARN("Stub; we set our timer resolution at minimum\n");
00457     }
00458     return 0;
00459 }
00460 
00461 /**************************************************************************
00462  *              timeGetTime    [MMSYSTEM.607]
00463  *              timeGetTime    [WINMM.@]
00464  */
00465 DWORD WINAPI timeGetTime(void)
00466 {
00467 #if defined(COMMENTOUTPRIORTODELETING)
00468     DWORD       count;
00469 
00470     /* FIXME: releasing the win16 lock here is a temporary hack (I hope)
00471      * that lets mciavi.drv run correctly
00472      */
00473     if (pFnReleaseThunkLock) pFnReleaseThunkLock(&count);
00474     if (pFnRestoreThunkLock) pFnRestoreThunkLock(count);
00475 #endif
00476 
00477     return GetTickCount();
00478 }

Generated on Sat May 26 2012 04:17:07 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.