Files
rtbrick-bngblaster/code/common/src/timer.c
T

664 lines
19 KiB
C
Raw Normal View History

2021-02-04 21:56:56 +01:00
/*
2022-03-08 16:51:41 +01:00
* A O(1) Timer library
2021-02-04 21:56:56 +01:00
*
* Hannes Gredler, July 2020
*
2023-01-13 16:01:38 +00:00
* Copyright (C) 2020-2023, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
2021-02-04 21:56:56 +01:00
*/
2022-03-08 16:51:41 +01:00
#include "timer.h"
#include "logging.h"
2021-02-04 21:56:56 +01:00
2022-09-15 16:21:57 +00:00
/**
* Set timer expiration.
*/
static void
timer_set_expire(timer_s *timer, time_t sec, long nsec)
{
timer->expire.tv_sec += sec;
timer->expire.tv_nsec += nsec;
/* Handle nsec overflow. */
2022-10-17 20:20:21 +00:00
if(timer->expire.tv_nsec >= 1e9) {
2022-09-15 16:21:57 +00:00
timer->expire.tv_nsec -= 1e9;
timer->expire.tv_sec++;
}
timer->expired = false;
}
/**
* Compare two timespecs.
*
* return -1 if ts1 is older than ts2
* return +1 if ts1 is newer than ts2
* return 0 if ts1 is equal to ts2
*/
static int
timespec_compare(struct timespec *ts1, struct timespec *ts2)
{
if(ts1->tv_sec < ts2->tv_sec) {
return -1;
}
if(ts1->tv_sec > ts2->tv_sec) {
return +1;
}
if(ts1->tv_nsec < ts2->tv_nsec) {
return -1;
}
if(ts1->tv_nsec > ts2->tv_nsec) {
return +1;
}
return 0;
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Add two timestamps x and y, storing the result in result.
*/
void
2022-03-08 16:51:41 +01:00
timespec_add(struct timespec *result, struct timespec *x, struct timespec *y)
2021-02-04 21:56:56 +01:00
{
2022-03-08 16:51:41 +01:00
result->tv_sec = x->tv_sec + y->tv_sec;
result->tv_nsec = x->tv_nsec + y->tv_nsec;
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Avoid overflow of result->tv_nsec */
2022-10-17 20:20:21 +00:00
if(result->tv_nsec >= 1e9) {
2022-03-08 16:51:41 +01:00
result->tv_nsec -= 1e9;
result->tv_sec += 1;
}
2021-02-04 21:56:56 +01:00
}
2021-10-26 13:01:54 +02:00
/**
2021-04-12 16:41:32 +02:00
* Subtract the timestamps x from y, storing the result in result.
2021-02-04 21:56:56 +01:00
*/
void
2022-03-08 16:51:41 +01:00
timespec_sub(struct timespec *result, struct timespec *x, struct timespec *y)
2021-02-04 21:56:56 +01:00
{
2022-03-08 16:51:41 +01:00
2022-09-16 16:02:14 +00:00
if(x->tv_sec < y->tv_sec) {
2021-02-04 21:56:56 +01:00
result->tv_sec = 0;
result->tv_nsec = 0;
return;
}
2022-03-08 16:51:41 +01:00
/* Avoid overflow of result->tv_nsec */
2022-09-16 16:02:14 +00:00
if(x->tv_nsec < y->tv_nsec) {
if(x->tv_sec == y->tv_sec) {
2021-04-12 16:41:32 +02:00
result->tv_sec = 0;
result->tv_nsec = 0;
return;
}
2021-03-29 22:46:48 +02:00
result->tv_nsec = x->tv_nsec + 1e9 - y->tv_nsec;
result->tv_sec = x->tv_sec - y->tv_sec - 1;
} else {
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_nsec = x->tv_nsec - y->tv_nsec;
}
2021-02-04 21:56:56 +01:00
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Format a timestamp in one of four buffers.
* This way we can format upto 4 timespecs in one printf() call.
*/
char *
2022-09-15 16:21:57 +00:00
timespec_format(struct timespec *x)
2021-02-04 21:56:56 +01:00
{
static char buffer[4][32];
static int idx = 0;
char *ret;
ret = buffer[idx];
idx = (idx+1) & 3;
snprintf(ret, 32, "%lu.%06lus", x->tv_sec, x->tv_nsec / 1000);
return ret;
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Enqueue a timer for change processing.
*/
2022-09-15 16:21:57 +00:00
static void
2022-09-02 13:55:09 +00:00
timer_change(timer_s *timer)
2021-02-04 21:56:56 +01:00
{
timer_root_s *timer_root;
2022-09-15 16:21:57 +00:00
/* Are we already on the timer change queue? */
if(timer->on_change_list) {
2022-01-24 21:41:50 +01:00
return;
2021-02-04 21:56:56 +01:00
}
timer_root = timer->timer_bucket->timer_root;
CIRCLEQ_INSERT_TAIL(&timer_root->timer_change_qhead, timer, timer_change_qnode);
timer->on_change_list = true;
}
2021-10-26 13:01:54 +02:00
static void
2022-09-02 13:55:09 +00:00
timer_enqueue_bucket(timer_root_s *root, timer_s *timer, time_t sec, long nsec)
2021-02-04 21:56:56 +01:00
{
timer_bucket_s *timer_bucket;
2022-03-08 16:51:41 +01:00
/* Find the bucket for insertion. */
2021-02-04 21:56:56 +01:00
CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) {
2022-10-17 20:20:21 +00:00
if(timer_bucket->sec != sec || timer_bucket->nsec != nsec) {
2021-02-04 21:56:56 +01:00
continue;
}
2022-03-08 16:51:41 +01:00
/* Found it! */
goto INSERT;
2021-02-04 21:56:56 +01:00
}
2022-03-08 16:51:41 +01:00
/* No bucket found that matches the timer values.
* Create a fresh bucket. */
2021-02-04 21:56:56 +01:00
timer_bucket = calloc(1, sizeof(timer_bucket_s));
2022-10-17 20:20:21 +00:00
if(!timer_bucket) {
2021-02-04 21:56:56 +01:00
return;
}
CIRCLEQ_INSERT_TAIL(&root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode);
CIRCLEQ_INIT(&timer_bucket->timer_qhead);
timer_bucket->sec = sec;
timer_bucket->nsec = nsec;
timer_bucket->timer_root = root;
root->buckets++;
#ifdef BNGBLASTER_TIMER_LOGGING
2021-02-04 21:56:56 +01:00
LOG(TIMER_DETAIL, "Add timer bucket %lu.%06lus\n",
timer_bucket->sec, timer_bucket->nsec/1000);
#endif
2021-02-04 21:56:56 +01:00
INSERT:
2021-02-04 21:56:56 +01:00
timer->timer_bucket = timer_bucket;
CIRCLEQ_INSERT_TAIL(&timer_bucket->timer_qhead, timer, timer_qnode);
timer_bucket->timers++;
}
2022-03-08 16:51:41 +01:00
/**
2021-10-26 13:01:54 +02:00
* Dequeue a timer from its timer_bucket.
*/
static void
2022-09-02 13:55:09 +00:00
timer_dequeue_bucket(timer_s *timer)
2021-10-26 13:01:54 +02:00
{
timer_root_s *timer_root;
timer_bucket_s *timer_bucket;
timer_bucket = timer->timer_bucket;
timer_root = timer_bucket->timer_root;
CIRCLEQ_REMOVE(&timer_bucket->timer_qhead, timer, timer_qnode);
timer_bucket->timers--;
timer->timer_bucket = NULL;
2022-03-08 16:51:41 +01:00
/* If the last timer of a bucket is gone,
* remove the bucket as well. */
2022-10-17 20:20:21 +00:00
if(!timer_bucket->timers) {
2021-10-26 13:01:54 +02:00
CIRCLEQ_REMOVE(&timer_root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode);
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, " Delete timer bucket %lu.%06lus\n",
timer_bucket->sec, timer_bucket->nsec/1000);
#endif
2021-10-26 13:01:54 +02:00
free(timer_bucket);
timer_root->buckets--;
}
}
static void
2022-09-02 13:55:09 +00:00
timer_requeue(timer_s *timer, time_t sec, long nsec)
2021-10-26 13:01:54 +02:00
{
timer_root_s *timer_root;
timer_bucket_s *timer_bucket;
timer_bucket = timer->timer_bucket;
timer_root = timer_bucket->timer_root;
timer_set_expire(timer, sec, nsec);
2022-03-08 16:51:41 +01:00
/* If the expiration {sec,nsec} matches the bucket, then simply
2021-10-26 13:01:54 +02:00
* timer dequeue and enqueue to keep correct temporal ordering.
* If there is no match, do a slightly more expensive
2022-03-08 16:51:41 +01:00
* bucket dequeue and enqueue. */
2022-10-17 20:20:21 +00:00
if(timer_bucket->sec == sec && timer_bucket->nsec == nsec) {
2021-10-26 13:01:54 +02:00
CIRCLEQ_REMOVE(&timer_bucket->timer_qhead, timer, timer_qnode);
CIRCLEQ_INSERT_TAIL(&timer_bucket->timer_qhead, timer, timer_qnode);
} else {
timer_dequeue_bucket(timer);
timer_enqueue_bucket(timer_root, timer, sec, nsec);
}
#ifdef BNGBLASTER_TIMER_LOGGING
LOG(TIMER_DETAIL, " Reset %s timer, expire in %lu.%06lus\n",
timer->name, sec, nsec/1000);
#endif
2021-10-26 13:01:54 +02:00
}
/**
2021-02-04 21:56:56 +01:00
* Smear all the timer of a given bucket to expire equi-distant.
* Call this function periodically to avoid clustering of timers.
*/
void
2022-09-02 13:55:09 +00:00
timer_smear_bucket(timer_root_s *root, time_t sec, long nsec)
2021-02-04 21:56:56 +01:00
{
timer_bucket_s *timer_bucket;
timer_s *timer, *last_timer;
struct timespec now, diff, step;
long step_nsec;
2022-03-08 16:51:41 +01:00
/* Find the bucket for smearing. */
2021-02-04 21:56:56 +01:00
CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) {
2022-10-17 20:20:21 +00:00
if(timer_bucket->sec != sec || timer_bucket->nsec != nsec) {
2021-02-04 21:56:56 +01:00
continue;
}
2022-03-08 16:51:41 +01:00
/* Found the bucket. Next compute the timespan
* between now and last timer. */
2021-02-04 21:56:56 +01:00
last_timer = CIRCLEQ_LAST(&timer_bucket->timer_qhead);
2022-10-17 20:20:21 +00:00
if(timer_bucket->timers > 1 && last_timer) {
2022-10-14 18:38:40 +02:00
clock_gettime(CLOCK_MONOTONIC, &now);
timespec_sub(&diff, &last_timer->expire, &now);
step_nsec = (diff.tv_sec * 1e9 + diff.tv_nsec) / (timer_bucket->timers); /* calculate smear step */
step.tv_sec = step_nsec / 1e9;
step.tv_nsec = step_nsec - (step.tv_sec * 1e9);
#ifdef BNGBLASTER_TIMER_LOGGING
2022-10-14 18:38:40 +02:00
LOG(TIMER_DETAIL, "Smear %u timers in bucket %lu.%06lus\n", timer_bucket->timers, sec, nsec);
LOG(TIMER_DETAIL, "Now %lu.%06lus, last expire %lu.%06lus, step %lu.%06lus\n",
now.tv_sec, now.tv_nsec / 1000,
last_timer->expire.tv_sec, last_timer->expire.tv_nsec / 1000,
step.tv_sec, step.tv_nsec / 1000);
#endif
2022-10-14 18:38:40 +02:00
/* Now walk all timers and space them <step> apart. */
CIRCLEQ_FOREACH(timer, &timer_bucket->timer_qhead, timer_qnode) {
2023-04-25 08:30:58 +00:00
if(timer->no_smear) continue;
2022-10-14 18:38:40 +02:00
timespec_add(&timer->expire, &now, &step);
now = timer->expire;
#ifdef BNGBLASTER_TIMER_LOGGING
2022-10-14 18:38:40 +02:00
LOG(TIMER_DETAIL, " Smear %s -> expire %lu.%06lus\n", timer->name,
timer->expire.tv_sec, timer->expire.tv_nsec / 1000);
#endif
2022-10-14 18:38:40 +02:00
}
2021-10-22 16:04:42 +02:00
}
2022-01-24 21:41:50 +01:00
return;
2021-10-22 16:04:42 +02:00
}
}
2021-10-26 13:01:54 +02:00
/**
* Smear all the timer of all buckets to expire equi-distant.
*/
2021-10-22 16:04:42 +02:00
void
2022-09-02 13:55:09 +00:00
timer_smear_all_buckets(timer_root_s *root)
2021-10-22 16:04:42 +02:00
{
timer_bucket_s *timer_bucket;
timer_s *timer, *last_timer;
struct timespec now, diff, step;
long step_nsec;
2022-10-14 18:38:40 +02:00
/* Iterate over all buckets for smearing. */
2021-10-22 16:04:42 +02:00
CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) {
2022-10-17 20:20:21 +00:00
/* Compute the time span between now and last timer. */
2021-10-22 16:04:42 +02:00
last_timer = CIRCLEQ_LAST(&timer_bucket->timer_qhead);
2022-10-17 20:20:21 +00:00
if(timer_bucket->timers > 1 && last_timer) {
clock_gettime(CLOCK_MONOTONIC, &now);
2022-10-14 18:38:40 +02:00
timespec_sub(&diff, &last_timer->expire, &now);
step_nsec = (diff.tv_sec * 1e9 + diff.tv_nsec) / (timer_bucket->timers); /* calculate smear step */
step.tv_sec = step_nsec / 1e9;
step.tv_nsec = step_nsec - (step.tv_sec * 1e9);
#ifdef BNGBLASTER_TIMER_LOGGING
2022-10-14 18:38:40 +02:00
LOG(TIMER_DETAIL, "Smear %u timers in bucket %lu.%06lus\n", timer_bucket->timers, timer_bucket->sec, timer_bucket->nsec);
LOG(TIMER_DETAIL, "Now %lu.%06lus, last expire %lu.%06lus, step %lu.%06lus\n",
now.tv_sec, now.tv_nsec / 1000,
last_timer->expire.tv_sec, last_timer->expire.tv_nsec / 1000,
step.tv_sec, step.tv_nsec / 1000);
#endif
2022-10-14 18:38:40 +02:00
/* Now walk all timers and space them <step> apart. */
CIRCLEQ_FOREACH(timer, &timer_bucket->timer_qhead, timer_qnode) {
2023-04-25 08:30:58 +00:00
if(timer->no_smear) continue;
timespec_add(&timer->expire, &now, &step);
now = timer->expire;
#ifdef BNGBLASTER_TIMER_LOGGING
2023-04-25 08:30:58 +00:00
LOG(TIMER_DETAIL, " Smear %s -> expire %lu.%06lus\n", timer->name,
last_timer->expire.tv_sec, last_timer->expire.tv_nsec / 1000);
#endif
2023-04-25 08:30:58 +00:00
2022-10-14 18:38:40 +02:00
}
2021-10-22 16:04:42 +02:00
}
2021-02-04 21:56:56 +01:00
}
}
2023-04-25 08:30:58 +00:00
void
timer_no_smear(timer_s *timer)
{
if(timer) {
timer->no_smear = true;
}
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* We do not delete timers, but rather dequeue them and move them to
* the garbage collection queue, where they may get recycled.
*/
2021-10-26 13:01:54 +02:00
static void
2022-09-02 13:55:09 +00:00
timer_del_internal(timer_s *timer)
2021-02-04 21:56:56 +01:00
{
timer_bucket_s *timer_bucket;
timer_root_s *timer_root;
2021-02-04 21:56:56 +01:00
#ifdef BNGBLASTER_TIMER_LOGGING
2021-02-04 21:56:56 +01:00
LOG(TIMER, " Delete %s timer\n", timer->name);
#endif
2021-02-04 21:56:56 +01:00
timer_bucket = timer->timer_bucket;
if(timer_bucket) {
timer_root = timer_bucket->timer_root;
timer_dequeue_bucket(timer);
/* Add to GC list */
CIRCLEQ_INSERT_TAIL(&timer_root->timer_gc_qhead, timer, timer_qnode);
timer_root->gc++;
2022-10-17 20:20:21 +00:00
if(timer->ptimer) {
2022-09-15 16:21:57 +00:00
*timer->ptimer = NULL; /* delete references to this timer */
timer->ptimer= NULL;
}
}
2021-02-04 21:56:56 +01:00
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Mark a timer for deletion.
*/
void
2022-09-02 13:55:09 +00:00
timer_del(timer_s *timer)
2021-02-04 21:56:56 +01:00
{
if(timer) {
timer->delete = true;
timer_change(timer);
}
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Deferred processing of all timers.
*/
2021-10-26 13:01:54 +02:00
static void
2022-09-02 13:55:09 +00:00
timer_process_changes(timer_root_s *root)
2021-02-04 21:56:56 +01:00
{
timer_s *timer;
timer_bucket_s *timer_bucket;
2022-09-15 16:21:57 +00:00
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
while(!CIRCLEQ_EMPTY(&root->timer_change_qhead)) {
2021-02-04 21:56:56 +01:00
timer = CIRCLEQ_FIRST(&root->timer_change_qhead);
timer_bucket = timer->timer_bucket;
2022-03-08 16:51:41 +01:00
/* Changes are only processed once.
* Take this timer off the change list. */
2021-02-04 21:56:56 +01:00
CIRCLEQ_REMOVE(&root->timer_change_qhead, timer, timer_change_qnode);
timer->on_change_list = false;
2022-09-15 16:21:57 +00:00
if(timer->delete) {
/* Delete. */
2021-02-04 21:56:56 +01:00
timer_del_internal(timer);
continue;
}
2022-09-15 16:21:57 +00:00
if(timer->periodic) {
/* Requeue. */
2022-10-14 16:28:57 +02:00
if(timer->reset) {
/* Reset timer start time. */
timer->expire.tv_sec = now.tv_sec;
timer->expire.tv_nsec = now.tv_nsec;
}
2021-02-04 21:56:56 +01:00
timer_requeue(timer, timer_bucket->sec, timer_bucket->nsec);
continue;
}
}
}
2021-10-26 13:01:54 +02:00
/**
2022-03-08 16:51:41 +01:00
* Enqueue a timer with a given callback function onto
* the hierarchical timer list.
2021-02-04 21:56:56 +01:00
*/
void
2022-09-15 16:21:57 +00:00
timer_add(timer_root_s *root, timer_s **ptimer, char *name,
time_t sec, long nsec,
void *data, void (*cb)(timer_s *))
2021-02-04 21:56:56 +01:00
{
2022-09-15 16:21:57 +00:00
timer_s *timer = *ptimer;
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* This timer already is enqueued. Requeue. */
2022-09-15 16:21:57 +00:00
if(timer) {
clock_gettime(CLOCK_MONOTONIC, &timer->expire);
2021-02-04 21:56:56 +01:00
timer_requeue(timer, sec, nsec);
2022-03-08 16:51:41 +01:00
/* Update data and cb if there was a change.
* Do the reformatting of name only during a change. */
2022-10-17 20:20:21 +00:00
if(timer->data != data || timer->cb != cb) {
strncpy(timer->name, name, sizeof(timer->name));
2021-02-17 22:06:32 +01:00
timer->data = data;
timer->cb = cb;
}
2022-01-24 21:41:50 +01:00
return;
2021-02-04 21:56:56 +01:00
}
2022-09-15 16:21:57 +00:00
if(CIRCLEQ_EMPTY(&root->timer_gc_qhead)) {
2022-03-08 16:51:41 +01:00
/* GC queue is empty, make a fresh allocation. */
2021-02-04 21:56:56 +01:00
timer = calloc(1, sizeof(timer_s));
} else {
2022-03-08 16:51:41 +01:00
/* Dequeue the first entry on the GC list and recycle. */
2021-02-04 21:56:56 +01:00
timer = CIRCLEQ_FIRST(&root->timer_gc_qhead);
CIRCLEQ_REMOVE(&root->timer_gc_qhead, timer, timer_qnode);
root->gc--;
2022-03-08 16:51:41 +01:00
memset(timer, 0, sizeof(timer_s));
2021-02-04 21:56:56 +01:00
}
2022-10-17 20:20:21 +00:00
if(!timer) {
2021-02-04 21:56:56 +01:00
return;
}
2022-03-08 16:51:41 +01:00
/* Store name, data, callback and misc. data. */
strncpy(timer->name, name, sizeof(timer->name));
2021-02-04 21:56:56 +01:00
timer->data = data;
timer->cb = cb;
2022-09-15 16:21:57 +00:00
clock_gettime(CLOCK_MONOTONIC, &timer->expire);
2021-02-04 21:56:56 +01:00
timer_set_expire(timer, sec, nsec);
timer->ptimer = ptimer;
*ptimer = timer;
2022-03-08 16:51:41 +01:00
/* Enqueue it into the correct timer bucket. */
2021-02-04 21:56:56 +01:00
timer_enqueue_bucket(root, timer, sec, nsec);
#ifdef BNGBLASTER_TIMER_LOGGING
2021-02-04 21:56:56 +01:00
LOG(TIMER, "Add %s timer, expire in %lu.%06lus\n", timer->name, sec, nsec/1000);
#endif
2021-02-04 21:56:56 +01:00
}
void
2022-03-08 16:51:41 +01:00
timer_add_periodic(timer_root_s *root, timer_s **ptimer, char *name,
2022-09-15 16:21:57 +00:00
time_t sec, long nsec,
void *data, void (*cb)(timer_s *))
2021-02-04 21:56:56 +01:00
{
timer_s *timer;
timer_add(root, ptimer, name, sec, nsec, data, cb);
timer = *ptimer;
2022-09-15 16:21:57 +00:00
if(timer) {
2021-02-04 21:56:56 +01:00
timer->periodic = true;
2022-10-14 16:28:57 +02:00
timer->reset = true;
2021-02-04 21:56:56 +01:00
}
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Process the timer queue.
2021-11-02 15:15:38 +01:00
*
2021-10-26 13:01:54 +02:00
* @param root timer root
2021-02-04 21:56:56 +01:00
*/
void
2022-03-08 16:51:41 +01:00
timer_walk(timer_root_s *root)
2021-02-04 21:56:56 +01:00
{
timer_s *timer;
timer_bucket_s *timer_bucket;
struct timespec now, min, sleep, rem;
int res;
/* No buckets filled and we're done. */
2022-10-17 20:20:21 +00:00
if(CIRCLEQ_EMPTY(&root->timer_bucket_qhead)) {
2021-10-26 13:01:54 +02:00
return;
}
2021-02-04 21:56:56 +01:00
2021-10-26 13:01:54 +02:00
clock_gettime(CLOCK_MONOTONIC, &now);
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, "Walk timer queue, now %lu.%06lus\n",
now.tv_sec, now.tv_nsec / 1000);
#endif
2021-10-26 13:01:54 +02:00
min.tv_sec = 0;
min.tv_nsec = 0;
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Walk all buckets. */
2021-10-26 13:01:54 +02:00
CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) {
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, " Checking timer bucket %lu.%06lus\n",
timer_bucket->sec, timer_bucket->nsec/1000);
#endif
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* First pass. Call into expired nodes. */
2021-10-26 13:01:54 +02:00
CIRCLEQ_FOREACH(timer, &timer_bucket->timer_qhead, timer_qnode) {
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Hitting the first non-expired timer means
* we're done processing this buckets queue. */
2022-09-15 16:21:57 +00:00
if(timespec_compare(&timer->expire, &now) == 1) {
2021-10-26 13:01:54 +02:00
break;
}
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Everything from here one is expired. */
2021-10-26 13:01:54 +02:00
timer->expired = true;
2022-03-08 16:51:41 +01:00
/* Execute callback. */
2022-09-15 16:21:57 +00:00
if(timer->cb) {
2022-11-01 10:33:26 +00:00
timer->timestamp = &now;
2022-09-15 16:21:57 +00:00
(*timer->cb)(timer);
#ifdef BNGBLASTER_TIMER_LOGGING
LOG(TIMER_DETAIL, " Firing %s timer\n", timer->name);
#endif
2021-10-26 13:01:54 +02:00
}
2021-02-04 21:56:56 +01:00
2022-09-15 16:21:57 +00:00
if(timer->periodic) {
2022-03-08 16:51:41 +01:00
/* Periodic timers are simple de-queued and
* re-inserted at the tail of this buckets queue. */
2021-10-26 13:01:54 +02:00
timer_change(timer);
} else {
2022-03-08 16:51:41 +01:00
/* Everything else gets deleted. */
2021-10-26 13:01:54 +02:00
timer_del(timer);
2021-02-04 21:56:56 +01:00
}
}
2021-10-26 13:01:54 +02:00
}
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Process all changes from the last timer run. */
2021-10-26 13:01:54 +02:00
timer_process_changes(root);
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Second pass. Figure out min sleep time. */
2021-10-26 13:01:54 +02:00
CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) {
CIRCLEQ_FOREACH(timer, &timer_bucket->timer_qhead, timer_qnode) {
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Ignore deleted timers that wait for change processing. */
2022-10-17 20:20:21 +00:00
if(timer->delete) {
2021-10-26 13:01:54 +02:00
continue;
}
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* First timer in the queue becomes the actual minimum. */
2022-10-17 20:20:21 +00:00
if(min.tv_sec == 0 && min.tv_nsec == 0) {
2021-10-26 13:01:54 +02:00
min.tv_sec = timer->expire.tv_sec;
min.tv_nsec = timer->expire.tv_nsec;
}
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Find the min timer. */
2022-10-17 20:20:21 +00:00
if(timespec_compare(&timer->expire, &min) == -1) {
2021-10-26 13:01:54 +02:00
min.tv_sec = timer->expire.tv_sec;
min.tv_nsec = timer->expire.tv_nsec;
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, "New minimum sleep (%s) timer, found %lu.%06lus\n",
timer->name, min.tv_sec, min.tv_nsec / 1000);
#endif
2021-10-26 13:01:54 +02:00
}
2022-03-08 16:51:41 +01:00
/* Hitting the first non-expired timer means
* we're done processing this buckets queue. */
2022-10-17 20:20:21 +00:00
if(timespec_compare(&timer->expire, &now) == 1) {
2021-10-26 13:01:54 +02:00
break;
2021-02-04 21:56:56 +01:00
}
}
2021-10-26 13:01:54 +02:00
}
2021-02-04 21:56:56 +01:00
2022-03-08 16:51:41 +01:00
/* Calculate the sleep timer. */
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, " Now %lu.%06lus\n", now.tv_sec, now.tv_nsec / 1000);
LOG(TIMER_DETAIL, " Min %lu.%06lus\n", min.tv_sec, min.tv_nsec / 1000);
#endif
2021-10-26 13:01:54 +02:00
clock_gettime(CLOCK_MONOTONIC, &now);
2022-09-15 16:21:57 +00:00
if(timespec_compare(&now, &min) == -1) {
2021-10-26 13:01:54 +02:00
timespec_sub(&sleep, &min, &now);
#ifdef BNGBLASTER_TIMER_LOGGING
2021-10-26 13:01:54 +02:00
LOG(TIMER_DETAIL, " Sleep %lu.%06lus\n", sleep.tv_sec, sleep.tv_nsec / 1000);
#endif
2021-02-04 21:56:56 +01:00
res = nanosleep(&sleep, &rem);
2022-09-15 16:21:57 +00:00
if(res == -1) {
switch (errno) {
case EINTR: /* Ctrl-C */
break;
default:
LOG(ERROR, "Timer Error: nanosleep %s (%d)\n", strerror(errno), errno);
2022-09-15 16:21:57 +00:00
break;
}
}
2021-02-04 21:56:56 +01:00
}
}
2021-10-26 13:01:54 +02:00
/**
2021-02-04 21:56:56 +01:00
* Init a timer root.
*
* @param root timer root
2021-02-04 21:56:56 +01:00
*/
void
2022-09-02 13:55:09 +00:00
timer_init_root(timer_root_s *timer_root)
2021-02-04 21:56:56 +01:00
{
CIRCLEQ_INIT(&timer_root->timer_bucket_qhead);
CIRCLEQ_INIT(&timer_root->timer_gc_qhead);
CIRCLEQ_INIT(&timer_root->timer_change_qhead);
}
/**
2021-02-04 21:56:56 +01:00
* Flush all timers hanging off a timer root.
*
* @param root timer root
2021-02-04 21:56:56 +01:00
*/
void
2022-09-02 13:55:09 +00:00
timer_flush_root(timer_root_s *timer_root)
2021-02-04 21:56:56 +01:00
{
timer_s *timer;
timer_bucket_s *timer_bucket;
/* First step. Walk all timers and move them onto the GC thread. */
2021-02-04 21:56:56 +01:00
CIRCLEQ_FOREACH(timer_bucket, &timer_root->timer_bucket_qhead, timer_bucket_qnode) {
2021-10-26 13:01:54 +02:00
CIRCLEQ_FOREACH(timer, &timer_bucket->timer_qhead, timer_qnode) {
timer_del(timer);
}
2021-02-04 21:56:56 +01:00
}
timer_process_changes(timer_root);
/* Second step. Run the GC queue. */
2022-09-15 16:21:57 +00:00
while(!CIRCLEQ_EMPTY(&timer_root->timer_gc_qhead)) {
2021-02-04 21:56:56 +01:00
timer = CIRCLEQ_FIRST(&timer_root->timer_gc_qhead);
CIRCLEQ_REMOVE(&timer_root->timer_gc_qhead, timer, timer_qnode);
2021-10-26 13:01:54 +02:00
timer_root->gc--;
free(timer);
2021-02-04 21:56:56 +01:00
}
}