Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygentime.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
1.7.6.1
|