int main() { ULONG Min = ~0; ULONG Max = ~0; ULONG Cur = ~0; NtQueryTimerResolution(&Max, &Min, &Cur); printf("NtQueryTimerResolution -> Max=%08luns Min=%08luns Cur=%08luns\n", Min * 100, Max * 100, Cur * 100); #if 0 /* figure out the 100ns relative to the 1970 epoc. */ SYSTEMTIME st; st.wYear = 1970; st.wMonth = 1; st.wDayOfWeek = 4; /* Thor's day. */ st.wDay = 1; st.wHour = 0; st.wMinute = 0; st.wSecond = 0; st.wMilliseconds = 0; FILETIME ft; if (SystemTimeToFileTime(&st, &ft)) { printf("epoc is %I64u (0x%08x%08x)\n", ft, ft.dwHighDateTime, ft.dwLowDateTime); if (FileTimeToSystemTime(&ft, &st)) printf("unix epoc: %d-%02d-%02d %02d:%02d:%02d.%03d (week day %d)\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, st.wDayOfWeek); else printf("FileTimeToSystemTime failed, lasterr=%d\n", GetLastError()); } else printf("SystemTimeToFileTime failed, lasterr=%d\n", GetLastError()); ft.dwHighDateTime = 0; ft.dwLowDateTime = 0; if (FileTimeToSystemTime(&ft, &st)) printf("nt time start: %d-%02d-%02d %02d:%02d:%02d.%03d (week day %d)\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, st.wDayOfWeek); else printf("FileTimeToSystemTime failed, lasterr=%d\n", GetLastError()); #endif return 0; }
DEFINE_SYSCALL(clock_getres, int, clk_id, struct timespec *, res) { log_info("clock_getres(%d, 0x%p)", clk_id, res); if (!mm_check_write(res, sizeof(struct timespec))) return -L_EFAULT; switch (clk_id) { case CLOCK_REALTIME: { ULONG coarse, fine, actual; NtQueryTimerResolution(&coarse, &fine, &actual); uint64_t ns = (uint64_t)actual * NANOSECONDS_PER_TICK; res->tv_sec = ns / NANOSECONDS_PER_SECOND; res->tv_nsec = ns % NANOSECONDS_PER_SECOND; return 0; } case CLOCK_MONOTONIC: case CLOCK_MONOTONIC_COARSE: case CLOCK_MONOTONIC_RAW: { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); uint64_t ns = (double)1. / (double)freq.QuadPart; if (ns == 0) { res->tv_sec = 0; res->tv_nsec = 1; } else { res->tv_sec = ns / NANOSECONDS_PER_SECOND; res->tv_nsec = ns % NANOSECONDS_PER_SECOND; } return 0; } default: return -L_EINVAL; } }
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; }