diff options
author | David Härdeman <david@hardeman.nu> | 2020-06-22 20:20:08 +0200 |
---|---|---|
committer | David Härdeman <david@hardeman.nu> | 2020-06-22 20:20:08 +0200 |
commit | 3d7ae10a541629727844163f7d64507baedd6c78 (patch) | |
tree | a31d9274c64c743cd944414ae4833b880283ffcc /ptimer.c | |
parent | dc83b9bf92439f0472333dca0bfa1f7edda689b4 (diff) |
Add a shared timer, remove timerfd usage from idle and announce
Diffstat (limited to 'ptimer.c')
-rw-r--r-- | ptimer.c | 208 |
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); +} + |