#ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include struct timer_handle { HANDLE htimer; uint64_t interval_ns; uint64_t qpc_frequency; uint64_t next_deadline; bool started; }; static inline uint64_t qpc_now_ns(uint64_t freq) { LARGE_INTEGER qpc; QueryPerformanceCounter(&qpc); return (uint64_t)((qpc.QuadPart * 1000000000ULL) / freq); } struct timer_handle *timer_new(uint64_t interval_ns) { struct timer_handle *t = (struct timer_handle *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct timer_handle)); if(!t) return 0; t->htimer = CreateWaitableTimerW(0, TRUE, 0); if(!t->htimer) { HeapFree(GetProcessHeap(), 0, t); return 0; } LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); t->qpc_frequency = freq.QuadPart; t->interval_ns = interval_ns; t->started = false; return t; } bool timer_start(struct timer_handle *t) { t->next_deadline = qpc_now_ns(t->qpc_frequency) + t->interval_ns; t->started = true; return true; } bool timer_wait(struct timer_handle *t) { if(!t->started) return false; uint64_t now = qpc_now_ns(t->qpc_frequency); if(t->next_deadline <= now) { t->next_deadline += t->interval_ns; return true; } uint64_t sleep_ns = t->next_deadline - now; if(sleep_ns > 500000) { // > 0.5ms LARGE_INTEGER due; due.QuadPart = -(int64_t)((sleep_ns - 500000) / 100); // 100ns units, negative = relative SetWaitableTimer(t->htimer, &due, 0, 0, 0, 0); WaitForSingleObject(t->htimer, INFINITE); } while(qpc_now_ns(t->qpc_frequency) < t->next_deadline) { YieldProcessor(); // pause instruction } t->next_deadline += t->interval_ns; return true; } void timer_destroy(struct timer_handle *t) { if(t) { if(t->htimer) CloseHandle(t->htimer); HeapFree(GetProcessHeap(), 0, t); } } #else // Linux #include #include #include #include #include #include struct timer_handle { int tfd; int epfd; struct itimerspec spec; int started; }; static struct timer_handle *timer_new(uint64_t interval_ns) { struct timer_handle *t = (struct timer_handle *)malloc(sizeof(struct timer_handle)); if(!t) return 0; t->tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); if(t->tfd < 0) { free(t); return 0; } t->epfd = epoll_create1(EPOLL_CLOEXEC); if(t->epfd < 0) { close(t->tfd); free(t); return 0; } struct epoll_event ev = { .events = EPOLLIN, .data = { .fd = t->tfd } }; epoll_ctl(t->epfd, EPOLL_CTL_ADD, t->tfd, &ev); t->spec.it_interval.tv_sec = interval_ns / 1000000000ULL; t->spec.it_interval.tv_nsec = interval_ns % 1000000000ULL; t->spec.it_value.tv_sec = 0; t->spec.it_value.tv_nsec = 0; t->started = 0; return t; } static bool timer_start(struct timer_handle *t) { if(t->started) return true; t->spec.it_value = t->spec.it_interval; if(timerfd_settime(t->tfd, 0, &t->spec, 0)) return false; t->started = 1; return true; } static bool timer_wait(struct timer_handle *t) { if(!t->started) return false; struct epoll_event ev; int r = epoll_wait(t->epfd, &ev, 1, -1); if(r < 0) return false; uint64_t expirations; read(t->tfd, &expirations, sizeof(expirations)); return true; } static void timer_destroy(struct timer_handle *t) { if(t) { if(t->tfd >= 0) close(t->tfd); if(t->epfd >= 0) close(t->epfd); free(t); } } #endif