summaryrefslogtreecommitdiff
path: root/ptimer.c
diff options
context:
space:
mode:
authorDavid Härdeman <david@hardeman.nu>2020-06-22 20:20:08 +0200
committerDavid Härdeman <david@hardeman.nu>2020-06-22 20:20:08 +0200
commit3d7ae10a541629727844163f7d64507baedd6c78 (patch)
treea31d9274c64c743cd944414ae4833b880283ffcc /ptimer.c
parentdc83b9bf92439f0472333dca0bfa1f7edda689b4 (diff)
Add a shared timer, remove timerfd usage from idle and announce
Diffstat (limited to 'ptimer.c')
-rw-r--r--ptimer.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/ptimer.c b/ptimer.c
new file mode 100644
index 0000000..7d8662d
--- /dev/null
+++ b/ptimer.c
@@ -0,0 +1,208 @@
+#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;
+
+ fprintf(stderr, "Got a tick of %u secs\n", diff);
+ list_for_each_entry_safe(ptask, ptmp, &ptimer->ptasks, list) {
+ if (ptask->remain > diff) {
+ ptask->remain -= diff;
+ continue;
+ }
+
+ ptask->cb(ptask);
+ ptask->remain = ptask->interval;
+ if (ptask->times > 0) {
+ ptask->times--;
+ if (ptask->times == 0)
+ 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;
+
+ if (list_empty(&ptimer->ptasks)) {
+ 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;
+ }
+
+ fprintf(stderr, "Lowest %u GCD is %u\n", lowest, array_gcd(intervals, i));
+ ptimer_set(lowest, array_gcd(intervals, i));
+}
+
+void
+ptimer_del_task(struct ptimer_task *ptask)
+{
+ struct ptimer *ptimer = cfg->ptimer;
+
+ assert_return(ptask && ptimer && ptimer->task_count > 0);
+
+ list_del(&ptask->list);
+ 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);
+
+ uring_task_get(&ptimer->task);
+ 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_ANN, "task %p, ptimer %p", task, ptimer);
+ xfree(ptimer);
+ cfg->ptimer = NULL;
+}
+
+void
+ptimer_delete()
+{
+ assert_return(cfg->ptimer);
+
+ debug(DBG_ANN, "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;
+ }
+
+ debug(DBG_IGMP, "Called");
+ 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);
+}
+