Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1427 lines
42 KiB

  1. /******************************************************************************
  2. Copyright (c) 1985-1999 Microsoft Corporation
  3. Title: TIME.C : WINMM TIMER API
  4. Version: 1.00
  5. History:
  6. 21 Feb 1992 - Robin Speed (RobinSp) converted to Windows NT
  7. *****************************************************************************/
  8. #include <nt.h>
  9. #include <ntrtl.h>
  10. #include <nturtl.h>
  11. #include "winmmi.h"
  12. #define _INC_ALL_WOWSTUFF
  13. #include "mmwow32.h"
  14. /****************************************************************************
  15. Structure shared between timer APIs and timer thread
  16. ****************************************************************************/
  17. #define TDD_MINRESOLUTION 55 // in milliseconds
  18. UINT TDD_MAXRESOLUTION; // Should be 2 ... But ...
  19. #define TDD_MAXPERIOD 1000000 // 1000 seconds
  20. #define TDD_MINPERIOD TDD_MAXRESOLUTION // Some apps assume this.
  21. #define TIMER_STACK_SIZE 300
  22. HANDLE hTimerThread; // we need this to be global
  23. #define ROUND_MIN_TIME_TO_MS(x) (((x) + 9900) / 10000) // Special sloppy round
  24. DWORD MinimumTime; // Kernel's version of the max res in 100ns units
  25. typedef volatile struct {
  26. UINT Delay; // App requested delay (ms)
  27. UINT Resolution; // App requested resolution (ms)
  28. LPTIMECALLBACK Callback; // Whom to call when timer fires
  29. DWORD_PTR User; // Data to pass back when timer fires
  30. UINT Id; // Id allocated (bottom 4 bits = slot
  31. // id.
  32. UINT Flags; // App's option flags
  33. HANDLE TimerHandle; // Handle given to APP
  34. DWORD ThreadId; // Id of requestor thread (WOW cleanup)
  35. LARGE_INTEGER FireTime; // Time it should fire
  36. BOOL IsWOW; // For WOW events
  37. } TIMER_EVENT;
  38. //
  39. // Data integrity
  40. //
  41. // Held while handling resolution. ResolutionCritSec should always be
  42. // held when using TimerData.PeriodSlots, TimerData.CurrentPeriod and
  43. // TimerData.CurrentActualPeriod.
  44. CRITICAL_SECTION ResolutionCritSec;
  45. // This critical section should be held when using Events,
  46. // TimerData.TimerNotCallingCallbackEvent, TimerData.CallbackTimerID
  47. // and TimerData.EventCount. The critical section should also be held
  48. // while creating the timer thread. This ensures that only one timer
  49. // thread is created. This critical section should not be acquired if
  50. // a thread already owns the ResolutionCritSec. A deadlock will occur
  51. // if this critical section is acquired after the ResolutionCritSec
  52. // is acquired.
  53. CRITICAL_SECTION TimerThreadCritSec;
  54. DWORD TimerThreadId;
  55. //
  56. // Data used to communicate with timer thread and within timer thread
  57. //
  58. struct {
  59. //
  60. // Thread control (timerThread)
  61. //
  62. HANDLE Event1; // Synch event - schedules thread
  63. BOOL Started; // So WOW Cleanup doesn't deadlock
  64. UINT CallbackTimerID; // The ID of the timer which is currently calling its' callback function.
  65. // This value is only valid if TimerCallingCallback is TRUE.
  66. BOOL TimerCallingCallback; // TRUE if a timer is calling its' callback function on the timer thread.
  67. // Otherwise FALSE.
  68. HANDLE TimerNotCallingCallbackEvent; // This event is set if no timer is calling its' callback function on
  69. // the timer thread. Otherwise it is not set.
  70. //
  71. // timeGetTime stuff
  72. //
  73. BOOL UseTickCount;
  74. LARGE_INTEGER InitialInterruptTick;
  75. DWORD StartTick;
  76. DWORD MinResolution;
  77. //
  78. // Internal to thread
  79. //
  80. UINT CurrentPeriod; // Current min res in ms
  81. DWORD CurrentActualPeriod;
  82. // What the kernel gave us in ms
  83. // units
  84. DWORD ThreadToKill; // For WOW cleanup
  85. WORD EventCount; // For returning (fairly) unique handles
  86. // Make this WORD for WOW compatiblity
  87. WORD PeriodSlots[TDD_MINRESOLUTION];
  88. // Count of what periods are set
  89. } TimerData;
  90. #define MAX_TIMER_EVENTS 16
  91. TIMER_EVENT Events[MAX_TIMER_EVENTS];
  92. /****************************************************************************
  93. Internal functions
  94. ****************************************************************************/
  95. BOOL TimeInitThread(void);
  96. void TimerCompletion(UINT TimerId);
  97. BOOL timeSetTimerEvent(TIMER_EVENT *pEvent);
  98. DWORD timeThread(LPVOID lpParameter);
  99. LRESULT timeThreadSetEvent(TIMER_EVENT *pEvent);
  100. void InitializeWaitEventArrays
  101. (
  102. UINT* pcObjects,
  103. HANDLE aTimers[MAX_TIMER_EVENTS + 1],
  104. UINT aEventIndexToTimerIDTable[MAX_TIMER_EVENTS+1]
  105. );
  106. /*
  107. ** Read the interrupt time from the kernel
  108. */
  109. static LONGLONG __inline ReadInterruptTick(VOID) {
  110. LARGE_INTEGER InterruptTime;
  111. // Copy the interrupt time, verifying that the 64 bit quantity (copied
  112. // in two 32 bit operations) remains valid.
  113. // This may mean we need to iterate around the loop.
  114. do {
  115. InterruptTime.HighPart = USER_SHARED_DATA->InterruptTime.High1Time;
  116. InterruptTime.LowPart = USER_SHARED_DATA->InterruptTime.LowPart;
  117. } while (InterruptTime.HighPart != USER_SHARED_DATA->InterruptTime.High2Time);
  118. return InterruptTime.QuadPart;
  119. }
  120. /*
  121. ** Calibrate our timer
  122. */
  123. VOID CalibrateTimer(VOID)
  124. {
  125. //
  126. // Find out the current time(s)
  127. //
  128. UINT n = 100;
  129. // We calibrate the timer by making sure that the tick count and
  130. // interrupt tick count are in step with each other. Just in case
  131. // the hardware goes funny we put a limit on the number of times we
  132. // execute the loop.
  133. while (n) {
  134. DWORD EndTick;
  135. --n;
  136. TimerData.StartTick = GetCurrentTime();
  137. TimerData.InitialInterruptTick.QuadPart = ReadInterruptTick();
  138. EndTick = GetCurrentTime();
  139. if (EndTick == TimerData.StartTick) {
  140. dprintf2(("Timer calibrated, looped %d times", 100-n));
  141. break;
  142. }
  143. }
  144. }
  145. // Calling this effectively leaks WINMM and makes sure we never
  146. // go through the DLL exit routine
  147. // This is used so we don't deadlock with shutting down our global threads
  148. BOOL LoadWINMM()
  149. {
  150. TCHAR sz[1000];
  151. BOOL bOK = 0 != GetModuleFileName(ghInst, sz, sizeof(sz) / sizeof(sz[0]));
  152. if (bOK) {
  153. HINSTANCE hInst = LoadLibrary(sz);
  154. if (hInst != NULL) {
  155. // ASSERT(hInst == ghInst);
  156. } else {
  157. bOK = FALSE;
  158. }
  159. }
  160. return bOK;
  161. }
  162. /****************************************************************************
  163. @doc INTERNAL
  164. @api BOOL | TimeInit | This function initialises the timer services.
  165. @rdesc The return value is TRUE if the services are initialised, FALSE
  166. if an error occurs.
  167. @comm it is not a FATAL error if a timer driver is not installed, this
  168. routine will allways return TRUE
  169. ****************************************************************************/
  170. BOOL NEAR PASCAL TimeInit(void)
  171. {
  172. //
  173. // Find out the maximum timer resolution we can support
  174. //
  175. {
  176. DWORD MaximumTime;
  177. DWORD CurrentTime;
  178. TimerData.MinResolution = TDD_MINRESOLUTION;
  179. if (!NT_SUCCESS(NtQueryTimerResolution(
  180. &MaximumTime,
  181. &MinimumTime,
  182. &CurrentTime))) {
  183. TDD_MAXRESOLUTION = 10; // was 16 for NT 3.1, 10 for NT 3.5
  184. dprintf2(("Kernel timer : using default maximum resolution"));
  185. } else {
  186. dprintf2((" MaximumTime = %d", MaximumTime));
  187. dprintf2((" CurrentTime = %d", CurrentTime));
  188. if ((MaximumTime + 9999) / 10000 < TDD_MINRESOLUTION) {
  189. TimerData.MinResolution = (MaximumTime + 9999) / 10000;
  190. }
  191. //
  192. // On the x86 it's just over 1ms minimum to we allow a little
  193. // leeway
  194. //
  195. TDD_MAXRESOLUTION = max(1, ROUND_MIN_TIME_TO_MS(MinimumTime));
  196. }
  197. }
  198. //
  199. // Compute the relationship between our timer and the performance
  200. // counter
  201. //
  202. CalibrateTimer();
  203. //
  204. // Start out slowly !
  205. //
  206. TimerData.CurrentPeriod = TimerData.MinResolution;
  207. TimerData.CurrentActualPeriod = TimerData.CurrentPeriod;
  208. return TRUE;
  209. }
  210. /****************************************************************************
  211. @doc INTERNAL
  212. @api BOOL | TimeInitThread | This function initialises the timer thread.
  213. @rdesc The return value is TRUE if the services are initialised, FALSE
  214. if an error occurs.
  215. @comm it is not a FATAL error if a timer driver is not installed, this
  216. routine will allways return TRUE
  217. ****************************************************************************/
  218. BOOL TimeInitThread(void)
  219. {
  220. // Make sure winmm never gets unloaded
  221. if (!LoadWINMM()) {
  222. return FALSE;
  223. }
  224. //
  225. // Set up events and create our thread
  226. //
  227. if (!NT_SUCCESS(NtCreateEvent(&TimerData.Event1,
  228. EVENT_ALL_ACCESS,
  229. NULL,
  230. SynchronizationEvent,
  231. FALSE))) { // Not signalled
  232. return FALSE;
  233. }
  234. // Create an unnamed signaled manual reset event.
  235. TimerData.TimerNotCallingCallbackEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  236. // CreateEvent() returns NULL if an error occurs.
  237. if (!TimerData.TimerNotCallingCallbackEvent) {
  238. NtClose(TimerData.Event1);
  239. TimerData.Event1 = NULL;
  240. return FALSE;
  241. }
  242. //
  243. // The thread will start up and wait on Event1 (alertably)
  244. //
  245. hTimerThread = CreateThread(NULL,
  246. TIMER_STACK_SIZE,
  247. timeThread,
  248. NULL,
  249. THREAD_SET_INFORMATION,
  250. &TimerThreadId);
  251. if (!hTimerThread) {
  252. CloseHandle(TimerData.TimerNotCallingCallbackEvent);
  253. NtClose(TimerData.Event1);
  254. TimerData.TimerNotCallingCallbackEvent = NULL;
  255. TimerData.Event1 = NULL;
  256. return FALSE;
  257. }
  258. SetThreadPriority(hTimerThread, THREAD_PRIORITY_TIME_CRITICAL);
  259. return TRUE;
  260. }
  261. /****************************************************************************
  262. @doc EXTERNAL
  263. @api MMRESULT | timeGetSystemTime | This function retrieves the system time
  264. in milliseconds. The system time is the time elapsed since
  265. Windows was started.
  266. @parm LPMMTIME | lpTime | Specifies a far pointer to an <t MMTIME> data
  267. structure.
  268. @parm UINT | wSize | Specifies the size of the <t MMTIME> structure.
  269. @rdesc Returns zero.
  270. The system time is returned in the <e MMTIME.ms> field of the <t MMTIME>
  271. structure.
  272. @comm The time is always returned in milliseconds.
  273. @xref timeGetTime
  274. ****************************************************************************/
  275. MMRESULT APIENTRY timeGetSystemTime(LPMMTIME lpTime, UINT wSize)
  276. {
  277. //
  278. // !!!WARNING DS is not setup right!!! see above
  279. //
  280. if (wSize < sizeof(MMTIME))
  281. return TIMERR_STRUCT;
  282. if (!ValidateWritePointer(lpTime,wSize)) {
  283. return TIMERR_STRUCT;
  284. }
  285. lpTime->u.ms = timeGetTime();
  286. lpTime->wType = TIME_MS;
  287. return TIMERR_NOERROR;
  288. }
  289. /****************************************************************************
  290. @doc EXTERNAL
  291. @api UINT | timeSetEvent | This function sets up a timed callback event.
  292. The event can be a one-time event or a periodic event. Once activated,
  293. the event calls the specified callback function.
  294. @parm UINT | wDelay | Specifies the event period in milliseconds.
  295. If the delay is less than the minimum period supported by the timer,
  296. or greater than the maximum period supported by the timer, the
  297. function returns an error.
  298. @parm UINT | wResolution | Specifies the accuracy of the delay in
  299. milliseconds. The resolution of the timer event increases with
  300. smaller <p wResolution> values. To reduce system overhead, use
  301. the maximum <p wResolution> value appropriate for your application.
  302. @parm LPTIMECALLBACK | lpFunction | Specifies the procedure address of
  303. a callback function that is called once upon expiration of a one-shot
  304. event or periodically upon expiration of periodic events.
  305. @parm DWORD | dwUser | Contains user-supplied callback data.
  306. @parm UINT | wFlags | Specifies the type of timer event, using one of
  307. the following flags:
  308. @flag TIME_ONESHOT | Event occurs once, after <p wPeriod> milliseconds.
  309. @flag TIME_PERIODIC | Event occurs every <p wPeriod> milliseconds.
  310. @rdesc Returns an ID code that identifies the timer event. Returns
  311. NULL if the timer event was not created. The ID code is also passed to
  312. the callback function.
  313. @comm Using this function to generate a high-frequency periodic-delay
  314. event (with a period less than 10 milliseconds) can consume a
  315. significant portion of the system CPU bandwidth. Any call to
  316. <f timeSetEvent> for a periodic-delay timer
  317. must be paired with a call to <f timeKillEvent>.
  318. The callback function must reside in a DLL. You don't have to use
  319. <f MakeProcInstance> to get a procedure-instance address for the callback
  320. function.
  321. @cb void CALLBACK | TimeFunc | <f TimeFunc> is a placeholder for the
  322. application-supplied function name. The actual name must be exported by
  323. including it in the EXPORTS statement of the module-definition file for
  324. the DLL.
  325. @parm UINT | wID | The ID of the timer event. This is the ID returned
  326. by <f timeSetEvent>.
  327. @parm UINT | wMsg | Not used.
  328. @parm DWORD | dwUser | User instance data supplied to the <p dwUser>
  329. parameter of <f timeSetEvent>.
  330. @parm DWORD | dw1 | Not used.
  331. @parm DWORD | dw2 | Not used.
  332. @comm Because the callback is accessed at interrupt time, it must
  333. reside in a DLL, and its code segment must be specified as FIXED
  334. in the module-definition file for the DLL. Any data that the
  335. callback accesses must be in a FIXED data segment as well.
  336. The callback may not make any system calls except for <f PostMessage>,
  337. <f timeGetSystemTime>, <f timeGetTime>, <f timeSetEvent>,
  338. <f timeKillEvent>, <f midiOutShortMsg>,
  339. <f midiOutLongMsg>, and <f OutputDebugStr>.
  340. @xref timeKillEvent timeBeginPeriod timeEndPeriod
  341. ****************************************************************************/
  342. UINT APIENTRY timeSetEvent(UINT wDelay, UINT wResolution,
  343. LPTIMECALLBACK lpFunction, DWORD_PTR dwUser, UINT wFlags)
  344. {
  345. // verify the input flags
  346. // first remove the callback type, then check that only
  347. // time_periodic or time_oneshot are specified
  348. if (wFlags & ~(TIME_CALLBACK_TYPEMASK | TIME_ONESHOT | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS)) {
  349. return(0);
  350. }
  351. return timeSetEventInternal(wDelay, wResolution, lpFunction,
  352. dwUser, wFlags, FALSE);
  353. }
  354. UINT timeSetEventInternal(UINT wDelay, UINT wResolution,
  355. LPTIMECALLBACK lpFunction, DWORD_PTR dwUser, UINT wFlags, BOOL IsWOW)
  356. {
  357. UINT TimerId; // Our return value
  358. TIMER_EVENT Event; // Event data for thread
  359. // V_TCALLBACK(lpFunction, MMSYSERR_INVALPARAM);
  360. //
  361. // First check our parameters
  362. //
  363. if (wDelay > TDD_MAXPERIOD || wDelay < TDD_MINPERIOD) {
  364. return 0;
  365. }
  366. //
  367. // if resolution is 0 set default resolution, otherwise
  368. // make sure the resolution is in range
  369. //
  370. if (wResolution > TimerData.MinResolution) {
  371. wResolution = TimerData.MinResolution;
  372. } else {
  373. if (wResolution < TDD_MAXRESOLUTION) {
  374. wResolution = TDD_MAXRESOLUTION;
  375. }
  376. }
  377. if (wResolution > wDelay) {
  378. wResolution = TimerData.MinResolution;
  379. }
  380. //
  381. // Remember time if it's periodic so we get accurate long term
  382. // timing. Otherwise we'll just use the delay.
  383. //
  384. if ((wFlags & TIME_PERIODIC) || IsWOW) {
  385. Event.FireTime.QuadPart = ReadInterruptTick();
  386. }
  387. Event.Delay = wDelay;
  388. Event.Resolution = wResolution;
  389. Event.Callback = lpFunction;
  390. Event.User = dwUser;
  391. Event.Flags = wFlags;
  392. Event.ThreadId = GetCurrentThreadId(); // For WOW cleanup
  393. Event.IsWOW = IsWOW;
  394. //
  395. // Now set up the period to be used
  396. //
  397. if (timeBeginPeriod(wResolution) == MMSYSERR_NOERROR) {
  398. EnterCriticalSection(&TimerThreadCritSec);
  399. if (NULL == TimerData.Event1)
  400. {
  401. if (!TimeInitThread())
  402. {
  403. LeaveCriticalSection(&TimerThreadCritSec);
  404. return(0);
  405. }
  406. }
  407. TimerId = (UINT)timeThreadSetEvent(&Event);
  408. LeaveCriticalSection(&TimerThreadCritSec);
  409. //
  410. // If we didn't get a good id give up
  411. //
  412. if (TimerId == 0) {
  413. timeEndPeriod(wResolution);
  414. }
  415. } else {
  416. TimerId = 0;
  417. }
  418. return TimerId;
  419. }
  420. /****************************************************************************
  421. @doc EXTERNAL
  422. @api MMRESULT | timeGetDevCaps | This function queries the timer device to
  423. determine its capabilities.
  424. @parm LPTIMECAPS | lpTimeCaps | Specifies a far pointer to a
  425. <t TIMECAPS> structure. This structure is filled with information
  426. about the capabilities of the timer device.
  427. @parm UINT | wSize | Specifies the size of the <t TIMECAPS> structure.
  428. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if it fails
  429. to return the timer device capabilities.
  430. ****************************************************************************/
  431. MMRESULT APIENTRY timeGetDevCaps(LPTIMECAPS lpTimeCaps, UINT wSize)
  432. {
  433. if (wSize < sizeof(TIMECAPS)) {
  434. return TIMERR_NOCANDO;
  435. }
  436. if (!ValidateWritePointer(lpTimeCaps, wSize)) {
  437. return TIMERR_NOCANDO;
  438. }
  439. lpTimeCaps->wPeriodMin = TDD_MINPERIOD;
  440. lpTimeCaps->wPeriodMax = TDD_MAXPERIOD;
  441. return MMSYSERR_NOERROR;
  442. }
  443. /****************************************************************************
  444. @doc EXTERNAL
  445. @api MMRESULT | timeBeginPeriod | This function sets the minimum (lowest
  446. number of milliseconds) timer resolution that an application or
  447. driver is going to use. Call this function immediately before starting
  448. to use timer-event services, and call <f timeEndPeriod> immediately
  449. after finishing with the timer-event services.
  450. @parm UINT | wPeriod | Specifies the minimum timer-event resolution
  451. that the application or driver will use.
  452. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
  453. <p wPeriod> resolution value is out of range.
  454. @xref timeEndPeriod timeSetEvent
  455. @comm For each call to <f timeBeginPeriod>, you must call
  456. <f timeEndPeriod> with a matching <p wPeriod> value.
  457. An application or driver can make multiple calls to <f timeBeginPeriod>,
  458. as long as each <f timeBeginPeriod> call is matched with a
  459. <f timeEndPeriod> call.
  460. ****************************************************************************/
  461. MMRESULT APIENTRY timeBeginPeriod(UINT uPeriod)
  462. {
  463. dprintf3(("timeBeginPeriod %d", uPeriod));
  464. dprintf4((" CurrentPeriod = %d, CurrentActualPeriod = %d",
  465. TimerData.CurrentPeriod, TimerData.CurrentActualPeriod));
  466. //
  467. // See if period is in our range
  468. //
  469. if (uPeriod < TDD_MAXRESOLUTION) {
  470. return TIMERR_NOCANDO;
  471. }
  472. if (uPeriod >= TimerData.MinResolution) {
  473. return MMSYSERR_NOERROR;
  474. }
  475. EnterCriticalSection(&ResolutionCritSec);
  476. //
  477. // See what's happening in our slot
  478. //
  479. if (TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION] ==
  480. 0xFFFF) {
  481. //
  482. // Overflowed
  483. //
  484. LeaveCriticalSection(&ResolutionCritSec);
  485. return TIMERR_NOCANDO;
  486. }
  487. TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION]++;
  488. if (TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION] == 1 &&
  489. uPeriod < TimerData.CurrentActualPeriod) {
  490. DWORD NewPeriod100ns;
  491. //
  492. // Set the new period in our kernel driver handle
  493. // If it's just out then use the actual minimum
  494. //
  495. dprintf4(("timeBeginPeriod: setting resolution %d", uPeriod));
  496. NewPeriod100ns = uPeriod * 10000;
  497. if (NewPeriod100ns < MinimumTime) {
  498. NewPeriod100ns = MinimumTime;
  499. }
  500. if (!NT_SUCCESS(NtSetTimerResolution(
  501. NewPeriod100ns,
  502. TRUE,
  503. &NewPeriod100ns))) {
  504. dprintf1(("timeBeginPeriod: Failed to set period %d", uPeriod));
  505. TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION]--;
  506. LeaveCriticalSection(&ResolutionCritSec);
  507. return TIMERR_NOCANDO;
  508. } else {
  509. //
  510. // This slot is just started to be used and is higher
  511. // resolution that currently set
  512. //
  513. TimerData.CurrentPeriod = uPeriod;
  514. TimerData.CurrentActualPeriod =
  515. ROUND_MIN_TIME_TO_MS(NewPeriod100ns);
  516. LeaveCriticalSection(&ResolutionCritSec);
  517. return MMSYSERR_NOERROR;
  518. }
  519. } else {
  520. //
  521. // No need to set period as it's already set
  522. //
  523. LeaveCriticalSection(&ResolutionCritSec);
  524. return MMSYSERR_NOERROR;
  525. }
  526. }
  527. /****************************************************************************
  528. @doc EXTERNAL
  529. @api MMRESULT | timeEndPeriod | This function clears a previously set
  530. minimum (lowest number of milliseconds) timer resolution that an
  531. application or driver is going to use. Call this function
  532. immediately after using timer event services.
  533. @parm UINT | wPeriod | Specifies the minimum timer-event resolution
  534. value specified in the previous call to <f timeBeginPeriod>.
  535. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the specified
  536. <p wPeriod> resolution value is out of range.
  537. @xref timeBeginPeriod timeSetEvent
  538. @comm For each call to <f timeBeginPeriod>, you must call
  539. <f timeEndPeriod> with a matching <p wPeriod> value.
  540. An application or driver can make multiple calls to <f timeBeginPeriod>,
  541. as long as each <f timeBeginPeriod> call is matched with a
  542. <f timeEndPeriod> call.
  543. ****************************************************************************/
  544. MMRESULT APIENTRY timeEndPeriod(UINT uPeriod)
  545. {
  546. dprintf3(("timeEndPeriod %d", uPeriod));
  547. dprintf4((" CurrentPeriod = %d, CurrentActualPeriod = %d",
  548. TimerData.CurrentPeriod, TimerData.CurrentActualPeriod));
  549. //
  550. // Round the period to our range
  551. //
  552. if (uPeriod < TDD_MAXRESOLUTION) {
  553. return TIMERR_NOCANDO;
  554. }
  555. if (uPeriod >= TimerData.MinResolution) {
  556. return MMSYSERR_NOERROR;
  557. }
  558. EnterCriticalSection(&ResolutionCritSec);
  559. //
  560. // See what's happening in our slot
  561. //
  562. if (TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION] == 0) {
  563. //
  564. // Oops ! Overflowed
  565. //
  566. LeaveCriticalSection(&ResolutionCritSec);
  567. return TIMERR_NOCANDO;
  568. }
  569. TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION]--;
  570. if (TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION] == 0 &&
  571. uPeriod == TimerData.CurrentPeriod) {
  572. DWORD CurrentTime;
  573. //
  574. // This slot is just finished and was the fastest
  575. // so find the next fastest
  576. //
  577. for (;uPeriod < TimerData.MinResolution; uPeriod++) {
  578. if (TimerData.PeriodSlots[uPeriod - TDD_MAXRESOLUTION] != 0) {
  579. break;
  580. }
  581. }
  582. //
  583. // Reset the current setting
  584. //
  585. NtSetTimerResolution(TimerData.CurrentActualPeriod * 10000,
  586. FALSE,
  587. &CurrentTime);
  588. TimerData.CurrentActualPeriod = TimerData.MinResolution;
  589. TimerData.CurrentPeriod = uPeriod;
  590. if (uPeriod >= TimerData.MinResolution) {
  591. //
  592. // Nobody's interested in timing any more
  593. //
  594. } else {
  595. //
  596. // Set the new period in the kernel
  597. //
  598. DWORD NewPeriod100ns;
  599. //
  600. // Set the new period in our kernel driver handle
  601. //
  602. dprintf4(("timeEndPeriod: setting resolution %d", uPeriod));
  603. if (!NT_SUCCESS(NtSetTimerResolution(
  604. uPeriod * 10000,
  605. TRUE,
  606. &NewPeriod100ns))) {
  607. //
  608. // This guy's OK but everyone else is hosed
  609. //
  610. dprintf1(("timeEndPeriod: Failed to set period %d", uPeriod));
  611. } else {
  612. TimerData.CurrentActualPeriod = (NewPeriod100ns + 9999) / 10000;
  613. }
  614. }
  615. }
  616. LeaveCriticalSection(&ResolutionCritSec);
  617. return MMSYSERR_NOERROR;
  618. }
  619. /****************************************************************************
  620. @doc EXTERNAL
  621. @api MMRESULT | timeKillEvent | This functions destroys a specified timer
  622. callback event.
  623. @parm UINT | wID | Identifies the event to be destroyed.
  624. @rdesc Returns zero if successful. Returns TIMERR_NOCANDO if the
  625. specified timer event does not exist.
  626. @comm The timer event ID specified by <p wID> must be an ID
  627. returned by <f timeSetEvent>.
  628. @xref timeSetEvent
  629. ****************************************************************************/
  630. MMRESULT APIENTRY timeKillEvent(UINT uId)
  631. {
  632. MMRESULT mmr;
  633. TIMER_EVENT *pEvent;
  634. BOOL fWaitForCallbackToEnd;
  635. EnterCriticalSection(&TimerThreadCritSec);
  636. // This event will be initialized if timeSetEvent() was successfully called.
  637. if (NULL == TimerData.TimerNotCallingCallbackEvent) {
  638. LeaveCriticalSection(&TimerThreadCritSec);
  639. return TIMERR_NOCANDO;
  640. }
  641. pEvent = &Events[uId % MAX_TIMER_EVENTS];
  642. //
  643. // Find our event in the table and check it's there
  644. // This also catches already completed events
  645. //
  646. if (pEvent->Id != uId) {
  647. LeaveCriticalSection(&TimerThreadCritSec);
  648. return TIMERR_NOCANDO;
  649. }
  650. //
  651. // Release our event
  652. //
  653. timeEndPeriod(pEvent->Resolution);
  654. pEvent->Id = 0;
  655. if (!NT_SUCCESS(NtCancelTimer(pEvent->TimerHandle, NULL))) {
  656. mmr = TIMERR_NOCANDO;
  657. } else {
  658. mmr = MMSYSERR_NOERROR;
  659. }
  660. NtSetEvent(TimerData.Event1, NULL);
  661. fWaitForCallbackToEnd = ( TimerData.TimerCallingCallback &&
  662. (uId == TimerData.CallbackTimerID) &&
  663. (TimerThreadId != GetCurrentThreadId()) &&
  664. (pEvent->Flags & TIME_KILL_SYNCHRONOUS) );
  665. LeaveCriticalSection(&TimerThreadCritSec);
  666. if ((MMSYSERR_NOERROR == mmr) && fWaitForCallbackToEnd) {
  667. WaitForSingleObject(TimerData.TimerNotCallingCallbackEvent, INFINITE);
  668. }
  669. return mmr;
  670. }
  671. /****************************************************************************
  672. @doc EXTERNAL
  673. @api DWORD | timeGetTime | This function retrieves the system time
  674. in milliseconds. The system time is the time elapsed since
  675. Windows was started.
  676. @rdesc The return value is the system time in milliseconds.
  677. @comm The only difference between this function and
  678. the <f timeGetSystemTime> function is <f timeGetSystemTime>
  679. uses the standard multimedia time structure <t MMTIME> to return
  680. the system time. The <f timeGetTime> function has less overhead than
  681. <f timeGetSystemTime>.
  682. @xref timeGetSystemTime
  683. ****************************************************************************/
  684. DWORD APIENTRY timeGetTime(VOID)
  685. {
  686. if (TimerData.UseTickCount) {
  687. //
  688. // Use the system service
  689. //
  690. return GetCurrentTime();
  691. } else {
  692. LARGE_INTEGER Difference;
  693. Difference.QuadPart = ReadInterruptTick() - TimerData.InitialInterruptTick.QuadPart;
  694. return (DWORD)(Difference.QuadPart / 10000) + TimerData.StartTick;
  695. }
  696. }
  697. /****************************************************************************
  698. @doc INTERNAL
  699. @api LRESULT | timeThread | The timer thread
  700. @parm LPVOID | lpParameter | the thread parameter (NULL here)
  701. @rdesc Never returns
  702. @comm Note that this thread serializes access to the events list
  703. ****************************************************************************/
  704. #if _MSC_FULL_VER >= 13008827
  705. #pragma warning(push)
  706. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  707. #endif
  708. DWORD timeThread(LPVOID lpParameter)
  709. {
  710. NTSTATUS nts;
  711. UINT cObjects;
  712. UINT uiEventIDIndex;
  713. HANDLE aTimers[MAX_TIMER_EVENTS + 1];
  714. UINT aEventIndexToTimerIDTable[MAX_TIMER_EVENTS + 1];
  715. //
  716. // Tell people it's OK to call us from DLL init sections now
  717. //
  718. TimerData.Started = TRUE;
  719. InitializeWaitEventArrays( &cObjects, aTimers, aEventIndexToTimerIDTable );
  720. //
  721. // Sit in a loop waiting for something to do
  722. //
  723. for (;;) {
  724. nts = NtWaitForMultipleObjects(
  725. cObjects, // Number of objects (event + timers)
  726. aTimers, // Array of handles
  727. WaitAny, // Wait for any to signal
  728. TRUE, // Wait Alertably (???)
  729. NULL); // Wait forever
  730. if (STATUS_WAIT_0 == nts)
  731. {
  732. // There's been some timer change (timeSetEvent, timeKillEvent),
  733. // rebuild the array...
  734. InitializeWaitEventArrays( &cObjects, aTimers, aEventIndexToTimerIDTable );
  735. }
  736. else
  737. {
  738. if ((nts >= STATUS_WAIT_1) && (nts <= STATUS_WAIT_0 + MAX_TIMER_EVENTS))
  739. {
  740. uiEventIDIndex = nts - STATUS_WAIT_0;
  741. TimerCompletion(aEventIndexToTimerIDTable[uiEventIDIndex]);
  742. }
  743. else
  744. {
  745. WinAssert(FALSE);
  746. }
  747. }
  748. }
  749. return 1; // CreateThread() requires all threads to return a DWORD value. The
  750. // value this thread returns has no meaning.
  751. }
  752. #if _MSC_FULL_VER >= 13008827
  753. #pragma warning(pop)
  754. #endif
  755. void InitializeWaitEventArrays
  756. (
  757. UINT* pcObjects,
  758. HANDLE aTimers[MAX_TIMER_EVENTS + 1],
  759. UINT aEventIndexToTimerIDTable[MAX_TIMER_EVENTS+1]
  760. )
  761. {
  762. UINT cObjects;
  763. DWORD dwEventIndex;
  764. cObjects = 0;
  765. aTimers[cObjects++] = TimerData.Event1;
  766. EnterCriticalSection(&TimerThreadCritSec);
  767. for (dwEventIndex = 0; dwEventIndex < MAX_TIMER_EVENTS; dwEventIndex++)
  768. {
  769. if (0 != Events[dwEventIndex].Id)
  770. {
  771. aTimers[cObjects] = Events[dwEventIndex].TimerHandle;
  772. aEventIndexToTimerIDTable[cObjects] = Events[dwEventIndex].Id;
  773. cObjects++;
  774. }
  775. }
  776. *pcObjects = cObjects;
  777. LeaveCriticalSection(&TimerThreadCritSec);
  778. }
  779. /****************************************************************************
  780. @doc INTERNAL
  781. @api LRESULT | timeThread | The timer thread
  782. @parm PVOID | ApcContext | Our context - the wave buffer header
  783. @parm PIO_STATUS_BLOCK | The Io status block we used
  784. @rdesc None
  785. ****************************************************************************/
  786. BOOL timeSetTimerEvent(TIMER_EVENT *pEvent)
  787. {
  788. //
  789. // Work out time to fire (and store in case timer is periodic)
  790. //
  791. LONGLONG Delay;
  792. LARGE_INTEGER lDelay;
  793. //
  794. // Work out time to fire (and store in case timer is periodic)
  795. //
  796. pEvent->FireTime.QuadPart += pEvent->Delay*10000;
  797. if (pEvent->Flags & TIME_PERIODIC) {
  798. //
  799. // Note that this arithmetic must allow for the case where
  800. // timeGetTime() wraps. We do this by computing delay as
  801. // a signed quantity and testing the sign
  802. //
  803. Delay = ReadInterruptTick() - pEvent->FireTime.QuadPart;
  804. } else {
  805. Delay = -((LONGLONG)pEvent->Delay*10000);
  806. }
  807. //
  808. // If it's already fired then make the timer fire immediately
  809. // (or at least whichever is the latest - AD 1600 or now).
  810. // but DON'T call the callback now as we're in the TimerThreadCritSec!
  811. //
  812. if (Delay > 0) {
  813. // Delay = 0; we no longer use Delay after this point
  814. lDelay.QuadPart = 0;
  815. } else {
  816. lDelay.QuadPart = Delay;
  817. }
  818. //
  819. // Create a timer if we haven't got one
  820. //
  821. if (pEvent->TimerHandle == NULL) {
  822. HANDLE TimerHandle;
  823. if (!NT_SUCCESS(NtCreateTimer(
  824. &TimerHandle,
  825. TIMER_ALL_ACCESS,
  826. NULL,
  827. NotificationTimer))) {
  828. return FALSE;
  829. }
  830. pEvent->TimerHandle = TimerHandle;
  831. }
  832. //
  833. // Possibly valid since the timer API's are not synchronized anymore
  834. //
  835. // WinAssert(pEvent->Id != 0);
  836. //
  837. // Set up a system timer
  838. //
  839. return
  840. NT_SUCCESS(
  841. NtSetTimer(pEvent->TimerHandle,
  842. &lDelay,
  843. NULL,
  844. (PVOID)(DWORD_PTR)pEvent->Id,
  845. FALSE,
  846. 0,
  847. NULL));
  848. }
  849. /****************************************************************************
  850. @doc INTERNAL
  851. @api LRESULT | timeThreadSetEvent | Set a new event from the timer thread
  852. @parm TIMER_EVENT * | pEvent | Our Event
  853. @rdesc The new event id
  854. ****************************************************************************/
  855. LRESULT timeThreadSetEvent(TIMER_EVENT *pEvent)
  856. {
  857. UINT i;
  858. LRESULT lr = 0;
  859. EnterCriticalSection(&TimerThreadCritSec);
  860. //
  861. // Find a free slot and fill it
  862. //
  863. for (i = 0; i < MAX_TIMER_EVENTS; i++) {
  864. //
  865. // Is the slot free ?
  866. //
  867. if (Events[i].Id == 0) {
  868. pEvent->TimerHandle = Events[i].TimerHandle;
  869. Events[i] = *pEvent;
  870. do {
  871. TimerData.EventCount += MAX_TIMER_EVENTS;
  872. } while (TimerData.EventCount == 0);
  873. Events[i].Id = i + TimerData.EventCount;
  874. break; // Got our event
  875. }
  876. }
  877. if (i == MAX_TIMER_EVENTS) {
  878. lr = 0;
  879. } else {
  880. //
  881. // Set the new event in the driver
  882. //
  883. if (!timeSetTimerEvent(&Events[i])) {
  884. Events[i].Id = 0; // Failed so free our slot
  885. lr = 0;
  886. } else {
  887. lr = Events[i].Id;
  888. }
  889. }
  890. LeaveCriticalSection(&TimerThreadCritSec);
  891. // Notifying timer thread of changes..
  892. NtSetEvent(TimerData.Event1, NULL);
  893. return lr;
  894. }
  895. /****************************************************************************
  896. @doc INTERNAL
  897. @api void | TimerCompletion | Complete a timeout event
  898. @parm UINT | TimerId | Our timer handle
  899. @rdesc None
  900. ****************************************************************************/
  901. void TimerCompletion(UINT TimerId)
  902. {
  903. DWORD_PTR dpUser;
  904. TIMER_EVENT *pEvent;
  905. LPTIMECALLBACK pCallbackFunction;
  906. EnterCriticalSection(&TimerThreadCritSec);
  907. //
  908. // Find out where we are
  909. //
  910. pEvent = &Events[TimerId % MAX_TIMER_EVENTS];
  911. //
  912. // Synch up with timeKillEvent
  913. //
  914. if (pEvent->Id != TimerId) {
  915. LeaveCriticalSection(&TimerThreadCritSec);
  916. return;
  917. }
  918. if (pEvent->IsWOW) {
  919. //
  920. // Adobe Premiere has to be sure the time has reached the time
  921. // it expected. But because the timer we use for timeGetTime is
  922. // not the same (or at least not rounded the same) as the one used
  923. // to set the events) this need not be the case here.
  924. //
  925. while(pEvent->FireTime.QuadPart - ReadInterruptTick() > 0) {
  926. Sleep(1);
  927. }
  928. }
  929. switch (pEvent->Flags & TIME_CALLBACK_TYPEMASK) {
  930. case TIME_CALLBACK_FUNCTION:
  931. TimerData.TimerCallingCallback = TRUE;
  932. TimerData.CallbackTimerID = pEvent->Id;
  933. ResetEvent(TimerData.TimerNotCallingCallbackEvent);
  934. dpUser = pEvent->User;
  935. pCallbackFunction = pEvent->Callback;
  936. LeaveCriticalSection(&TimerThreadCritSec);
  937. //
  938. // Call the callback
  939. //
  940. #ifdef _WIN64
  941. DriverCallback(
  942. *(PDWORD_PTR)&pCallbackFunction, // Function
  943. DCB_FUNCTION, // Type of callback
  944. (HDRVR)(DWORD_PTR)TimerId, // Handle
  945. 0, // msg = 0
  946. dpUser, // User data
  947. 0, // dw1 = 0
  948. 0); // dw2 = 0
  949. #else // !WIN64
  950. if (pEvent->IsWOW) {
  951. WOW32DriverCallback(
  952. *(DWORD *)&pCallbackFunction, // Function
  953. DCB_FUNCTION, // Type of callback
  954. LOWORD(TimerId), // Handle
  955. 0, // msg = 0
  956. (DWORD)dpUser, // User data
  957. 0, // dw1 = 0
  958. 0); // dw2 = 0
  959. } else {
  960. DriverCallback(
  961. *(PDWORD_PTR)&pCallbackFunction, // Function
  962. DCB_FUNCTION, // Type of callback
  963. (HDRVR)TimerId, // Handle
  964. 0, // msg = 0
  965. dpUser, // User data
  966. 0, // dw1 = 0
  967. 0); // dw2 = 0
  968. }
  969. #endif // !WIN64
  970. EnterCriticalSection(&TimerThreadCritSec);
  971. TimerData.TimerCallingCallback = FALSE;
  972. SetEvent(TimerData.TimerNotCallingCallbackEvent);
  973. break;
  974. case TIME_CALLBACK_EVENT_SET:
  975. SetEvent((HANDLE)pEvent->Callback);
  976. break;
  977. case TIME_CALLBACK_EVENT_PULSE:
  978. PulseEvent((HANDLE)pEvent->Callback);
  979. break;
  980. }
  981. //
  982. // The callback may have kill it, created new timers etc!
  983. //
  984. if (TimerId == pEvent->Id) {
  985. if (!(pEvent->Flags & TIME_PERIODIC)) {
  986. UINT uResolution;
  987. //
  988. // One-shot - so destroy the event
  989. //
  990. uResolution = pEvent->Resolution; // Before we release the slot!
  991. pEvent->Id = 0;
  992. timeEndPeriod(uResolution);
  993. // Not renewing the timer should remove it from the list...
  994. NtSetEvent(TimerData.Event1, NULL);
  995. } else {
  996. //
  997. // Try repeating the event
  998. //
  999. if (!timeSetTimerEvent(pEvent)) {
  1000. UINT uResolution;
  1001. //
  1002. // Failed - so don't keep event hanging around
  1003. //
  1004. uResolution = pEvent->Resolution; // Before we release the slot!
  1005. pEvent->Id = 0;
  1006. timeEndPeriod(pEvent->Resolution);
  1007. }
  1008. } // Periodic processing
  1009. }
  1010. LeaveCriticalSection(&TimerThreadCritSec);
  1011. }
  1012. /****************************************************************************
  1013. @doc INTERNAL
  1014. @api void | TimerCleanup | Cleanup on thread termination or DLL unload
  1015. @parm PVOID | ThreadId | Thread to clean up (WOW) or 0 for DLL unload
  1016. @rdesc None
  1017. ****************************************************************************/
  1018. void TimeCleanup(DWORD ThreadId)
  1019. {
  1020. //
  1021. // Always called from DLL init routine which is protected by process
  1022. // semaphore so TimerData.ThreadToKill needs no extra protection
  1023. // This variable is an input to the timer thread which either terminates
  1024. // all timers or just those associated with the current thread (for WOW).
  1025. //
  1026. TimerData.ThreadToKill = ThreadId;
  1027. //
  1028. // Thread id of 0 means DLL cleanup
  1029. //
  1030. if (ThreadId == 0) {
  1031. if (hTimerThread) {
  1032. #ifdef WRONG
  1033. //
  1034. // we also can not synchronize with the thread at ALL ! It may not
  1035. // have gone through DLL initialization ! This means that during
  1036. // our dll routines we can not do anything with the thread unless
  1037. // we know for a fact the status of the thread !
  1038. //
  1039. // This could be fixed by setting a flag when the timer thread
  1040. // goes through initialization (process mutex held) and testing
  1041. // that flag here - but we don't exepect people to set timer
  1042. // events and unload winmm.dll
  1043. //
  1044. if (TimerData.Started) {
  1045. //
  1046. // Kill any events (only for current thread if WOW).
  1047. //
  1048. {
  1049. int i;
  1050. for (i = 0; i < MAX_TIMER_EVENTS; i++) {
  1051. if (Events[i].Id &&
  1052. (TimerData.ThreadToKill == 0 ||
  1053. TimerData.ThreadToKill == Events[i].ThreadId)) {
  1054. timeKillEvent(Events[i].Id);
  1055. }
  1056. }
  1057. }
  1058. }
  1059. // WaitForSingleObject(hTimerThread, -1);
  1060. // We cannot wait for the thread to terminate as it will
  1061. // not go through DLL exit processing while we are doing
  1062. // our DLL exit processing
  1063. #endif
  1064. }
  1065. if (TimerData.Event1) {
  1066. NtClose(TimerData.Event1);
  1067. }
  1068. } else {
  1069. //
  1070. // Per-thread Cleanup for WOW. We don't touch anything if it
  1071. // looks like nothing has run yet (so we might be caught out
  1072. // if the thread is stopped in the middle of a timeSetEvent).
  1073. //
  1074. if (TimerData.Started) {
  1075. //
  1076. // Kill any events (only for current thread if WOW).
  1077. //
  1078. {
  1079. int i;
  1080. for (i = 0; i < MAX_TIMER_EVENTS; i++) {
  1081. if (Events[i].Id &&
  1082. (TimerData.ThreadToKill == 0 ||
  1083. TimerData.ThreadToKill == Events[i].ThreadId)) {
  1084. timeKillEvent(Events[i].Id);
  1085. }
  1086. }
  1087. }
  1088. }
  1089. }
  1090. }