void OS::Timing::Initialize () { #if (defined _WIN32 || defined _WIN64 || defined __CYGWIN__) ULONG currentRes; NtSetTimerResolution (1, TRUE, ¤tRes); #endif }
RTDECL(int) RTTimerCreate(PRTTIMER *ppTimer, unsigned uMilliesInterval, PFNRTTIMER pfnTimer, void *pvUser) { #ifndef USE_WINMM /* * On windows we'll have to set the timer resolution before * we start the timer. */ ULONG ulMax = UINT32_MAX; ULONG ulMin = UINT32_MAX; ULONG ulCur = UINT32_MAX; NtQueryTimerResolution(&ulMax, &ulMin, &ulCur); Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur)); if (ulCur > ulMin && ulCur > 10000 /* = 1ms */) { if (NtSetTimerResolution(10000, TRUE, &ulCur) >= 0) Log(("Changed timer resolution to 1ms.\n")); else if (NtSetTimerResolution(20000, TRUE, &ulCur) >= 0) Log(("Changed timer resolution to 2ms.\n")); else if (NtSetTimerResolution(40000, TRUE, &ulCur) >= 0) Log(("Changed timer resolution to 4ms.\n")); else if (ulMin <= 50000 && NtSetTimerResolution(ulMin, TRUE, &ulCur) >= 0) Log(("Changed timer resolution to %lu *100ns.\n", ulMin)); else { AssertMsgFailed(("Failed to configure timer resolution!\n")); return VERR_INTERNAL_ERROR; } } #endif /* !USE_WINN */ /* * Create new timer. */ int rc = VERR_IPE_UNINITIALIZED_STATUS; PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer)); if (pTimer) { pTimer->u32Magic = RTTIMER_MAGIC; pTimer->pvUser = pvUser; pTimer->pfnTimer = pfnTimer; pTimer->iTick = 0; pTimer->uMilliesInterval = uMilliesInterval; #ifdef USE_WINMM /* sync kill doesn't work. */ pTimer->TimerId = timeSetEvent(uMilliesInterval, 0, rttimerCallback, (DWORD_PTR)pTimer, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); if (pTimer->TimerId) { ULONG ulMax = UINT32_MAX; ULONG ulMin = UINT32_MAX; ULONG ulCur = UINT32_MAX; NtQueryTimerResolution(&ulMax, &ulMin, &ulCur); Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur)); *ppTimer = pTimer; return VINF_SUCCESS; } rc = VERR_INVALID_PARAMETER; #else /* !USE_WINMM */ /* * Create Win32 event semaphore. */ pTimer->iError = 0; pTimer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL); if (pTimer->hTimer) { #ifdef USE_APC /* * Create wait semaphore. */ pTimer->hevWait = CreateEvent(NULL, FALSE, FALSE, NULL); if (pTimer->hevWait) #endif { /* * Kick off the timer thread. */ rc = RTThreadCreate(&pTimer->Thread, rttimerCallback, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer"); if (RT_SUCCESS(rc)) { /* * Wait for the timer to successfully create the timer * If we don't get a response in 10 secs, then we assume we're screwed. */ rc = RTThreadUserWait(pTimer->Thread, 10000); if (RT_SUCCESS(rc)) { rc = pTimer->iError; if (RT_SUCCESS(rc)) { *ppTimer = pTimer; return VINF_SUCCESS; } } ASMAtomicXchgU32(&pTimer->u32Magic, RTTIMER_MAGIC + 1); RTThreadWait(pTimer->Thread, 250, NULL); CancelWaitableTimer(pTimer->hTimer); } #ifdef USE_APC CloseHandle(pTimer->hevWait); #endif } CloseHandle(pTimer->hTimer); } #endif /* !USE_WINMM */ AssertMsgFailed(("Failed to create timer uMilliesInterval=%d. rc=%d\n", uMilliesInterval, rc)); RTMemFree(pTimer); } else rc = VERR_NO_MEMORY; return rc; }
RT_C_DECLS_END int main(int argc, char **argv) { int rc = RTR3InitExe(argc, &argv, 0); if (RT_FAILURE(rc)) return RTMsgInitFailure(rc); /* * Parse arguments. */ bool fVerbose = true; uint32_t u32NewRes = 0; uint32_t cSecsSleep = UINT32_MAX; static const RTGETOPTDEF s_aOptions[] = { { "--resolution", 'r', RTGETOPT_REQ_UINT32 }, { "--sleep", 's', RTGETOPT_REQ_UINT32 }, { "--quiet", 'q', RTGETOPT_REQ_NOTHING }, { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, }; RTGETOPTUNION ValueUnion; RTGETOPTSTATE GetState; RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0); while ((rc = RTGetOpt(&GetState, &ValueUnion))) { switch (rc) { case 'r': u32NewRes = ValueUnion.u32; if (u32NewRes > 16*10000 /* 16 ms */ || u32NewRes < 1000 /* 100 microsec */) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "syntax error: the new timer resolution (%RU32) is out of range\n", u32NewRes); break; case 's': cSecsSleep = ValueUnion.u32; break; case 'q': fVerbose = false; break; case 'v': fVerbose = true; break; case 'h': RTPrintf("Usage: ntsetfreq [-q|--quiet] [-v|--verbose] [-r|--resolution <100ns>] [-s|--sleep <1s>]\n"); return RTEXITCODE_SUCCESS; default: return RTGetOptPrintError(rc, &ValueUnion); } } /* * Query the current resolution. */ ULONG Cur = ~0; ULONG Min = ~0; ULONG Max = ~0; LONG Status; if (fVerbose || !u32NewRes) { Status = NtQueryTimerResolution(&Min, &Max, &Cur); if (Status >= 0) RTMsgInfo("cur: %u (%u.%02u Hz) min: %u (%u.%02u Hz) max: %u (%u.%02u Hz)\n", Cur, 10000000 / Cur, (10000000 / (Cur * 100)) % 100, Min, 10000000 / Min, (10000000 / (Min * 100)) % 100, Max, 10000000 / Max, (10000000 / (Max * 100)) % 100); else RTMsgError("NTQueryTimerResolution failed with status %#x\n", Status); } if (u32NewRes) { Status = NtSetTimerResolution(u32NewRes, TRUE, &Cur); if (Status < 0) RTMsgError("NTSetTimerResolution(%RU32,,) failed with status %#x\n", u32NewRes, Status); else if (fVerbose) { Cur = Min = Max = ~0; Status = NtQueryTimerResolution(&Min, &Max, &Cur); if (Status >= 0) RTMsgInfo("new: %u (%u.%02u Hz) requested %RU32 (%u.%02u Hz)\n", Cur, 10000000 / Cur, (10000000 / (Cur * 100)) % 100, u32NewRes, 10000000 / u32NewRes, (10000000 / (u32NewRes * 100)) % 100); else RTMsgError("NTSetTimerResolution succeeded but the NTQueryTimerResolution call failed with status %#x (ignored)\n", Status); Status = 0; } } if (u32NewRes && Status >= 0) { if (cSecsSleep == UINT32_MAX) for (;;) RTThreadSleep(RT_INDEFINITE_WAIT); else while (cSecsSleep-- > 0) RTThreadSleep(1000); } return Status >= 0 ? 0 : 1; }