From 8f82f7cf703d47341b49999d6369cae4817a9d1f Mon Sep 17 00:00:00 2001 From: Hannes Gredler Date: Wed, 14 Jan 2026 15:23:25 +0000 Subject: [PATCH 1/3] defer freeing empty buckets where it is safe to walk --- code/common/src/timer.c | 46 ++++++++++++++++++++++++++--------------- code/common/src/timer.h | 8 +++++++ 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/code/common/src/timer.c b/code/common/src/timer.c index 64d7529e..7a9dfe1b 100644 --- a/code/common/src/timer.c +++ b/code/common/src/timer.c @@ -188,29 +188,17 @@ timer_enqueue_bucket(timer_root_s *root, timer_s *timer, time_t sec, long nsec) static void timer_dequeue_bucket(timer_s *timer) { - 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; - /* If the last timer of a bucket is gone, - * remove the bucket as well. */ - if(!timer_bucket->timers) { - CIRCLEQ_REMOVE(&timer_root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode); - -#ifdef BNGBLASTER_TIMER_LOGGING - LOG(TIMER_DETAIL, " Delete timer bucket %lu.%06lus\n", - timer_bucket->sec, timer_bucket->nsec/1000); -#endif - - free(timer_bucket); - timer_root->buckets--; - } + /* + * Defer deleting empty buckets to timer_process_changes(). + */ } static void @@ -357,11 +345,14 @@ static void timer_process_changes(timer_root_s *root) { timer_s *timer; - timer_bucket_s *timer_bucket; + timer_bucket_s *timer_bucket, *timer_bucket_next; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); + /* + * First work the chnaged timer list. + */ while(!CIRCLEQ_EMPTY(&root->timer_change_qhead)) { timer = CIRCLEQ_FIRST(&root->timer_change_qhead); timer_bucket = timer->timer_bucket; @@ -388,6 +379,27 @@ timer_process_changes(timer_root_s *root) continue; } } + + /* + * Some buckets may be empty by now. + * Trash them here where it is safe. + */ + CIRCLEQ_FOREACH_SAFE(timer_bucket, &root->timer_bucket_qhead, + timer_bucket_qnode, timer_bucket_next) { + + /* If the bucket is empty, remove it. */ + if(!timer_bucket->timers) { + CIRCLEQ_REMOVE(&root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode); + +#ifdef BNGBLASTER_TIMER_LOGGING + LOG(TIMER_DETAIL, " Delete timer bucket %lu.%06lus\n", + timer_bucket->sec, timer_bucket->nsec/1000); +#endif + + free(timer_bucket); + root->buckets--; + } + } } /** @@ -629,4 +641,4 @@ timer_flush_root(timer_root_s *timer_root) timer_root->gc--; free(timer); } -} \ No newline at end of file +} diff --git a/code/common/src/timer.h b/code/common/src/timer.h index 6c09667a..92d5d206 100644 --- a/code/common/src/timer.h +++ b/code/common/src/timer.h @@ -14,6 +14,14 @@ #define MSEC 1000000 /* 1 million nanoseconds == 1 msec */ #define SEC 1000000000 /* 1 billion nanoseconds == 1 sec */ +#ifndef CIRCLEQ_FOREACH_SAFE +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST((head)); \ + (var) != (void *)(head) && \ + ((tvar) = CIRCLEQ_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + /* Top level data structure for timers. */ typedef struct timer_root_ { From 995f7573ff915cff5cd2883f0bab3b29c3be146a Mon Sep 17 00:00:00 2001 From: Hannes Gredler Date: Wed, 14 Jan 2026 15:50:44 +0000 Subject: [PATCH 2/3] move the empty bucket check into timer_walk() where it is more efficient. --- code/common/src/timer.c | 53 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/code/common/src/timer.c b/code/common/src/timer.c index 7a9dfe1b..01fb0973 100644 --- a/code/common/src/timer.c +++ b/code/common/src/timer.c @@ -338,6 +338,23 @@ timer_del(timer_s *timer) } } +/** + * Delete a timer bucket + */ +void +timer_del_bucket(timer_root_s *root, timer_bucket_s *timer_bucket) +{ + CIRCLEQ_REMOVE(&root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode); + +#ifdef BNGBLASTER_TIMER_LOGGING + LOG(TIMER_DETAIL, " Delete timer bucket %lu.%06lus\n", + timer_bucket->sec, timer_bucket->nsec/1000); +#endif + + free(timer_bucket); + root->buckets--; +} + /** * Deferred processing of all timers. */ @@ -345,14 +362,11 @@ static void timer_process_changes(timer_root_s *root) { timer_s *timer; - timer_bucket_s *timer_bucket, *timer_bucket_next; + timer_bucket_s *timer_bucket; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - /* - * First work the chnaged timer list. - */ while(!CIRCLEQ_EMPTY(&root->timer_change_qhead)) { timer = CIRCLEQ_FIRST(&root->timer_change_qhead); timer_bucket = timer->timer_bucket; @@ -379,27 +393,6 @@ timer_process_changes(timer_root_s *root) continue; } } - - /* - * Some buckets may be empty by now. - * Trash them here where it is safe. - */ - CIRCLEQ_FOREACH_SAFE(timer_bucket, &root->timer_bucket_qhead, - timer_bucket_qnode, timer_bucket_next) { - - /* If the bucket is empty, remove it. */ - if(!timer_bucket->timers) { - CIRCLEQ_REMOVE(&root->timer_bucket_qhead, timer_bucket, timer_bucket_qnode); - -#ifdef BNGBLASTER_TIMER_LOGGING - LOG(TIMER_DETAIL, " Delete timer bucket %lu.%06lus\n", - timer_bucket->sec, timer_bucket->nsec/1000); -#endif - - free(timer_bucket); - root->buckets--; - } - } } /** @@ -484,7 +477,7 @@ void timer_walk(timer_root_s *root) { timer_s *timer; - timer_bucket_s *timer_bucket; + timer_bucket_s *timer_bucket, *timer_bucket_next; struct timespec now, min, sleep, rem; int res; @@ -504,7 +497,8 @@ timer_walk(timer_root_s *root) min.tv_nsec = 0; /* Walk all buckets. */ - CIRCLEQ_FOREACH(timer_bucket, &root->timer_bucket_qhead, timer_bucket_qnode) { + CIRCLEQ_FOREACH_SAFE(timer_bucket, &root->timer_bucket_qhead, + timer_bucket_qnode, timer_bucket_next) { #ifdef BNGBLASTER_TIMER_LOGGING LOG(TIMER_DETAIL, " Checking timer bucket %lu.%06lus\n", @@ -541,6 +535,11 @@ timer_walk(timer_root_s *root) timer_del(timer); } } + + /* If the bucket is empty, remove it. */ + if(!timer_bucket->timers) { + timer_del_bucket(root, timer_bucket); + } } /* Process all changes from the last timer run. */ From 20808f9b398e005697e827c0408561820b939e3f Mon Sep 17 00:00:00 2001 From: Hannes Gredler Date: Thu, 15 Jan 2026 08:45:26 +0000 Subject: [PATCH 3/3] fix comment in which function empty buckets do get processed --- code/common/src/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/common/src/timer.c b/code/common/src/timer.c index 01fb0973..7ef32556 100644 --- a/code/common/src/timer.c +++ b/code/common/src/timer.c @@ -197,7 +197,7 @@ timer_dequeue_bucket(timer_s *timer) timer->timer_bucket = NULL; /* - * Defer deleting empty buckets to timer_process_changes(). + * Defer deleting empty buckets to timer_walk(). */ }