summaryrefslogtreecommitdiff
path: root/minecproxy/ptimer.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-23 20:56:22 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-23 20:56:22 +0200
commitea053d96f7e89e053d4af8d39b04c5428760345f (patch)
tree8182ca73675ad3933b0f38cb48a99c69101309b4 /minecproxy/ptimer.c
parent8c27290245b7bcc7cd2f72f3b4a7562294b43bbe (diff)
Big renaming, move some more functionality to shared lib
Diffstat (limited to 'minecproxy/ptimer.c')
-rw-r--r--minecproxy/ptimer.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/minecproxy/ptimer.c b/minecproxy/ptimer.c
new file mode 100644
index 0000000..5f9cf5d
--- /dev/null
+++ b/minecproxy/ptimer.c
@@ -0,0 +1,223 @@
+#include <inttypes.h>
+#include <sys/timerfd.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "main.h"
+#include "uring.h"
+#include "ptimer.h"
+
+struct ptimer {
+ uint64_t value;
+ time_t previous_time;
+ struct uring_task task;
+ unsigned task_count;
+ struct list_head ptasks;
+};
+
+static void
+ptimer_set(unsigned value, unsigned interval)
+{
+ struct itimerspec tspec = {
+ .it_interval = {
+ .tv_sec = interval,
+ .tv_nsec = 0
+ },
+ .it_value = {
+ .tv_sec = value,
+ .tv_nsec = 0
+ }
+ };
+
+ assert_return(cfg->ptimer && cfg->ptimer->task.fd >= 0);
+
+ if (timerfd_settime(cfg->ptimer->task.fd, 0, &tspec, NULL) != 0)
+ error("timerfd_settime: %m");
+}
+
+static unsigned
+gcd(unsigned a, unsigned b)
+{
+ if (a == 0)
+ return b;
+ return gcd(b % a, a);
+}
+
+static unsigned
+array_gcd(unsigned arr[], unsigned n)
+{
+ unsigned result = arr[0];
+
+ for (unsigned i = 1; i < n; i++)
+ result = gcd(arr[i], result);
+
+ return result;
+}
+
+static void
+ptimer_tick(struct ptimer *ptimer)
+{
+ time_t now = time(NULL);
+ unsigned diff = (unsigned)(now - ptimer->previous_time);
+ struct ptimer_task *ptask, *ptmp;
+
+ debug(DBG_TIMER, "got a tick of %u secs", diff);
+ list_for_each_entry_safe(ptask, ptmp, &ptimer->ptasks, list) {
+ if (ptask->remain > diff) {
+ ptask->remain -= diff;
+ continue;
+ }
+
+ debug(DBG_TIMER, "triggering ptask %p (times %u)",
+ ptask, ptask->times);
+
+ ptask->cb(ptask);
+ ptask->remain = ptask->interval;
+
+ if (ptask->times == 0)
+ continue;
+
+ ptask->times--;
+ if (ptask->times == 0) {
+ ptask->active = false;
+ list_del(&ptask->list);
+ }
+ }
+
+ ptimer->previous_time = now;
+}
+
+static void
+ptimer_reconfig(struct ptimer *ptimer)
+{
+ struct ptimer_task *ptask;
+ unsigned i = 0;
+ unsigned lowest = ~0;
+ unsigned interval;
+
+ if (list_empty(&ptimer->ptasks)) {
+ debug(DBG_TIMER, "no tasks");
+ ptimer_set(0, 0);
+ return;
+ }
+
+ unsigned intervals[ptimer->task_count];
+
+ list_for_each_entry(ptask, &ptimer->ptasks, list) {
+ if (ptask->remain < lowest)
+ lowest = ptask->remain;
+ intervals[i++] = ptask->interval;
+ }
+
+ interval = array_gcd(intervals, i);
+
+ debug(DBG_TIMER, "lowest: %u, gcd: %u\n", lowest, interval);
+ ptimer_set(lowest, interval);
+}
+
+void
+ptimer_del_task(struct ptimer_task *ptask)
+{
+ struct ptimer *ptimer = cfg->ptimer;
+
+ assert_return(ptask && ptimer);
+ assert_return_silent(ptask->active);
+ assert_return(ptimer->task_count > 0);
+
+ list_del(&ptask->list);
+ ptask->active = false;
+ ptimer->task_count--;
+ ptimer_tick(ptimer);
+ ptimer_reconfig(ptimer);
+ uring_task_put(&ptimer->task);
+}
+
+void
+ptimer_add_task(struct ptimer_task *ptask)
+{
+ struct ptimer *ptimer = cfg->ptimer;
+
+ assert_return(ptask && ptask->interval > 0 && ptask->cb && ptimer);
+ assert_return_silent(!ptask->active);
+
+ uring_task_get(&ptimer->task);
+ ptask->active = true;
+ ptask->remain = ptask->interval;
+ ptimer_tick(ptimer);
+ list_add(&ptask->list, &ptimer->ptasks);
+ ptimer->task_count++;
+ ptimer_reconfig(ptimer);
+}
+
+void
+ptimer_refdump()
+{
+ assert_return(cfg->ptimer);
+
+ uring_task_refdump(&cfg->ptimer->task);
+}
+
+static void
+ptimer_free(struct uring_task *task)
+{
+ struct ptimer *ptimer = container_of(task, struct ptimer, task);
+
+ assert_return(task);
+
+ debug(DBG_TIMER, "task %p, ptimer %p", task, ptimer);
+ xfree(ptimer);
+ cfg->ptimer = NULL;
+}
+
+void
+ptimer_delete()
+{
+ assert_return(cfg->ptimer);
+
+ debug(DBG_TIMER, "closing fd %i", cfg->ptimer->task.fd);
+ uring_task_destroy(&cfg->ptimer->task);
+}
+
+static void
+ptimer_cb(struct uring_task *task, int res)
+{
+ struct ptimer *ptimer = container_of(task, struct ptimer, task);
+
+ assert_return(task);
+ assert_task_alive(DBG_IGMP, task);
+
+ if (res != sizeof(ptimer->value)) {
+ error("timerfd_read: res: %i, %m", res);
+ return;
+ }
+
+ ptimer_tick(ptimer);
+ uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb);
+}
+
+void
+ptimer_init()
+{
+ struct ptimer *ptimer;
+ int tfd;
+
+ assert_return(!cfg->ptimer);
+
+ ptimer = zmalloc(sizeof(*ptimer));
+ if (!ptimer)
+ die("malloc: %m");
+
+ tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (tfd < 0)
+ die("timerfd_create: %m");
+
+ ptimer->task_count = 0;
+ ptimer->previous_time = time(NULL);
+ list_init(&ptimer->ptasks);
+ uring_task_init(&ptimer->task, "ptimer", uring_parent(), ptimer_free);
+ uring_task_set_fd(&ptimer->task, tfd);
+ cfg->ptimer = ptimer;
+ uring_read(&ptimer->task, &ptimer->value, sizeof(ptimer->value), ptimer_cb);
+}
+