summaryrefslogtreecommitdiff
path: root/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'timer.c')
-rw-r--r--timer.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/timer.c b/timer.c
new file mode 100644
index 0000000..96eb453
--- /dev/null
+++ b/timer.c
@@ -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