diff options
| author | Peter Fors <peter.fors@mindkiller.com> | 2025-04-03 20:02:00 +0200 |
|---|---|---|
| committer | Peter Fors <peter.fors@mindkiller.com> | 2025-04-03 20:02:00 +0200 |
| commit | 6274071e3857c1640cc5aef804cb86509ab312f9 (patch) | |
| tree | 1a4e56b3c3b4bfb4d8f0d2f588487d6e227c3b27 /timer.c | |
| parent | 971e51eebbf088f1ac590da1fc57e803eb1ee8cf (diff) | |
Move to glfw
Diffstat (limited to 'timer.c')
| -rw-r--r-- | timer.c | 149 |
1 files changed, 149 insertions, 0 deletions
@@ -0,0 +1,149 @@ + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdint.h> +#include <stdbool.h> + +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 <sys/timerfd.h> +#include <sys/epoll.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> + +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 |
