24#define _TIMER_PRIVATE 1
27#include <freeradius-devel/util/debug.h>
28#include <freeradius-devel/util/time.h>
29#include <freeradius-devel/util/dlist.h>
30#include <freeradius-devel/util/event.h>
31#include <freeradius-devel/util/value.h>
32#include <freeradius-devel/util/lst.h>
33#include <freeradius-devel/util/rb.h>
67#ifdef WITH_EVENT_DEBUG
105#define CHECK_PARENT(_ev) \
106 fr_assert_msg(!(_ev)->parent || (*(_ev)->parent == ev), \
107 "Event %p, allocd %s[%d], parent field points to %p", (_ev), (_ev)->file, (_ev)->line, *(_ev)->parent);
174#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
260 if (!tl->
parent)
return 0;
316 tail = timer_tail(&tl->ordered);
322 if (
unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
343 if (ret < 0)
return ret;
413 if (
unlikely(ev->linked_ctx != ctx)) {
424 char const *err_file;
436 err_file =
"not-available";
444 "Event %p, allocd %s[%d], was not found in the event "
445 "list or deferred list when re-armed: %s", ev,
483 talloc_set_destructor(ev, NULL);
491 "Failed inserting event into deferred list")) {
498 if (
unlikely(ret < 0))
goto insert_failed;
537 free_on_fire, callback, uctx);
544 if (timer_in_list(&tl->
deferred,ev)) {
545 (void)timer_remove(&tl->
deferred, ev);
548 char const *err_file;
555 err_file =
"not-available";
564 "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
565 "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
586 if (!ev->
tl)
return 0;
593 (void)timer_remove(&ev->
tl->ordered, ev);
607 EVENT_DEBUG(
"Asked to disarm inactive timer %p (noop)", ev);
621 if (timer_in_list(&tl->
deferred,ev)) {
622 (void)timer_remove(&tl->
deferred, ev);
625 if (ret < 0)
return ret;
653 if (likely(ret == 0)) {
714 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
731 callback(tl, *when, uctx);
758 unsigned int fired = 0;
760 while ((ev = timer_head(&tl->ordered))) {
773 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
790 callback(tl, *when, uctx);
848 if (timer_num_elements(&tl->
deferred) > 0) {
884 return timer_head(&tl->ordered);
898 while((ev = timer_pop_head(&tl->
deferred))) {
900 timer_insert_head(&tl->
deferred, ev);
925 tail = timer_tail(&tl->ordered);
933 "Deferred event is earlier than the last event in the ordered list");
941 while ((ev = timer_pop_head((&tl->
deferred)))) {
942 timer_insert_tail(&tl->ordered, ev);
955 return timer_num_elements(&tl->ordered);
984 return num + timer_num_elements(&tl->
deferred);
998 if (ev)
return ev->
when;
1079#ifdef WITH_EVENT_REPORT
1103#if defined(WITH_EVENT_DEBUG) && !defined(NDEBUG)
1105 { 1 }, { 10 }, { 100 },
1106 { 1000 }, { 10000 }, { 100000 },
1107 { 1000000 }, { 10000000 }, { 100000000 },
1108 { 1000000000 }, { 10000000000 }, { 100000000000 },
1109 { 1000000000000 }, { 10000000000000 }, { 100000000000000 },
1110 { 1000000000000000 }, { 10000000000000000 }, { 100000000000000000 },
1113static const char *decade_names[18] = {
1114 "1ns",
"10ns",
"100ns",
1115 "1us",
"10us",
"100us",
1116 "1ms",
"10ms",
"100ms",
1117 "1s",
"10s",
"100s",
1118 "1Ks",
"10Ks",
"100Ks",
1119 "1Ms",
"10Ms",
"100Ms",
1127} fr_event_counter_t;
1129static int8_t timer_location_cmp(
void const *one,
void const *two)
1131 fr_event_counter_t
const *a = one;
1132 fr_event_counter_t
const *b = two;
1136 return CMP(a->line, b->line);
1146 fr_event_counter_t find = { .file = ev->
file, .line = ev->
line };
1147 fr_event_counter_t *counter;
1151 counter = talloc(locations[i], fr_event_counter_t);
1156 counter->file = ev->
file;
1157 counter->line = ev->
line;
1183 TALLOC_CTX *tmp_ctx;
1195 locations[i] =
fr_rb_inline_alloc(tmp_ctx, fr_event_counter_t, node, timer_location_cmp, NULL);
1196 if (!locations[i])
goto oom;
1208 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1217 for (ev = timer_head(&tl->ordered);
1219 ev = timer_next(&tl->ordered, ev)) {
1220 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1225 pthread_mutex_lock(&print_lock);
1232 if (!array[i])
continue;
1235 EVENT_DEBUG(
" events <= %5s : %zu", decade_names[i], array[i]);
1237 EVENT_DEBUG(
" events > %5s : %zu", decade_names[i - 1], array[i]);
1239 EVENT_DEBUG(
" events %5s - %5s : %zu", decade_names[i - 1], decade_names[i], array[i]);
1245 fr_event_counter_t *counter = talloc_get_type_abort(node, fr_event_counter_t);
1248 counter->count, counter->file, counter->line);
1251 pthread_mutex_unlock(&print_lock);
1263#define TIMER_DUMP(_ev) \
1264 EVENT_DEBUG("%s[%d]: %p time=%" PRId64 " (%c), callback=%p", \
1265 (_ev)->file, (_ev)->line, _ev, fr_time_unwrap((_ev)->when), \
1266 fr_time_gt(now, (_ev)->when) ? '<' : '>', (_ev)->callback);
1285 for (ev = timer_head(&tl->ordered);
1287 ev = timer_next(&tl->ordered, ev)) {
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define NDEBUG_LOCATION_FMT
#define CC_NO_UBSAN(_sanitize)
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
#define NDEBUG_LOCATION_VALS
#define NDEBUG_LOCATION_ARGS
Pass caller information to the function.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
#define FR_DLIST_TYPEDEFS(_name, _head, _entry)
Define friendly names for type specific dlist head and entry structures.
Head of a doubly linked list.
Entry in a doubly linked list.
void * fr_lst_iter_next(fr_lst_t *lst, fr_lst_iter_t *iter)
Get the next entry in an LST.
int fr_lst_extract(fr_lst_t *lst, void *data)
Remove an element from an LST.
void * fr_lst_iter_init(fr_lst_t *lst, fr_lst_iter_t *iter)
Iterate over entries in LST.
int fr_lst_insert(fr_lst_t *lst, void *data)
unsigned int fr_lst_num_elements(fr_lst_t *lst)
void * fr_lst_peek(fr_lst_t *lst)
#define fr_lst_talloc_alloc(_ctx, _cmp, _talloc_type, _field, _init)
Creates an LST that verifies elements are of a specific talloc type.
fr_lst_index_t fr_lst_iter_t
unsigned int fr_lst_index_t
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
#define fr_rb_inline_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black tree.
Iterator structure for in-order traversal of an rbtree.
The main red black tree structure.
#define fr_time()
Allow us to arbitrarily manipulate time.
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
#define fr_time_gteq(_a, _b)
static int8_t fr_time_delta_cmp(fr_time_delta_t a, fr_time_delta_t b)
Compare two fr_time_delta_t values.
static int64_t fr_time_unwrap(fr_time_t time)
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
#define fr_time_wrap(_time)
#define fr_time_eq(_a, _b)
#define fr_time_add(_a, _b)
Add a time/time delta together.
#define fr_time_gt(_a, _b)
#define fr_time_sub(_a, _b)
Subtract one time from another.
#define fr_time_lt(_a, _b)
static int8_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
A time delta, a difference in time measured in nanoseconds.
TALLOC_CTX * linked_ctx
talloc ctx this event was bound to.
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
static uint64_t timer_list_ordered_num_events(fr_timer_list_t *tl)
static fr_timer_list_t * timer_list_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
int fr_timer_list_disarm(fr_timer_list_t *tl)
Disable all timers in a list.
int(* timer_disarm_t)(fr_timer_t *ev)
Specialisation function to delete a timer.
timer_head_t deferred
A list of timer events to be inserted, after the current batch has been processed.
fr_time_t fr_timer_when(fr_timer_t *ev)
Internal timestamp representing when the timer should fire.
uint64_t(* timer_list_num_elements_t)(fr_timer_list_t *tl)
Return the number of elements in the list.
timer_list_num_elements_t num_events
Function to get the number of elements in the list.
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
fr_timer_t ** parent
A pointer to the parent structure containing the timer event.
static int timer_list_lst_deferred(fr_timer_list_t *tl)
Insert a timer event into a the lst.
static void _parent_timer_cb(UNUSED fr_timer_list_t *parent_tl, fr_time_t when, void *uctx)
This callback fires in the parent to execute events in this sublist.
static int _timer_free(fr_timer_t *ev)
Remove an event from the event loop.
static int timer_ordered_insert_at(fr_timer_list_t *tl, fr_timer_t *ev)
Insert an event into an ordered timer list.
fr_time_t fr_timer_list_when(fr_timer_list_t *tl)
Return the time of the next event.
fr_timer_list_t * fr_timer_list_ordered_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new sorted event timer list.
static int timer_lst_insert_at(fr_timer_list_t *tl, fr_timer_t *ev)
Insert a timer event into a single event timer list.
bool _fr_timer_armed(fr_timer_t *ev)
Check if a timer event is armed.
fr_timer_t *(* timer_list_head_t)(fr_timer_list_t *tl)
Return the soonest timer event.
timer_disarm_t disarm
Function to delete a timer event.
static int _timer_list_free(fr_timer_list_t *tl)
Cleanup all timers currently in the list.
void const * uctx
Context pointer to pass to the callback.
int(* timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when)
Specialisation function to execute any pending timers.
timer_list_head_t head
Function to get the head of the list.
static int8_t timer_cmp(void const *a, void const *b)
Compare two timer events to see which one should occur first.
int fr_timer_list_force_run(fr_timer_list_t *tl)
Get the head of the timer list, the event may not be ready to fire.
fr_timer_cb_t callback
Callback to execute when the timer fires.
static int timer_lst_disarm(fr_timer_t *ev)
int(* timer_list_deferred_t)(fr_timer_list_t *tl)
Process any deferred timer events.
fr_time_t when
When this timer should fire.
struct fr_timer_list_pub_s pub
Public interface to the event timer list.
int _fr_timer_in(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p, fr_time_delta_t delta, bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
Insert a timer event into an event list.
static int timer_list_parent_update(fr_timer_list_t *tl)
Utility function to update parent timers.
timer_insert_t insert
Function to insert a timer event.
fr_timer_t * parent_ev
Event in the parent's event loop.
bool free_on_fire
Whether to free the event when it fires.
fr_timer_entry_t entry
Entry in a list of timer events.
static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled timer events in a lst.
fr_timer_list_t * fr_timer_list_lst_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new lst based timer list.
static timer_list_funcs_t const timer_funcs[]
Functions for performing operations on various types of timer list.
static int timer_ordered_disarm(fr_timer_t *ev)
Remove a timer from a timer list, but don't free it.
int fr_timer_disarm(fr_timer_t *ev)
Remove an event from the event list, but don't free the memory.
fr_timer_list_t * parent
Parent list to insert event into (if any).
#define CHECK_PARENT(_ev)
static int timer_list_ordered_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled events in an ordered list.
static fr_timer_t * timer_list_lst_head(fr_timer_list_t *tl)
Return the head of the event list.
static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl)
timer_list_type_t
What type of event list the timer is inserted into.
@ TIMER_LIST_TYPE_LST
Self-sorting timer list based on a left leaning skeleton tree.
@ TIMER_LIST_TYPE_ORDERED
Strictly ordered list of events in a dlist.
int line
Line this event was last updated on.
void fr_timer_list_set_time_func(fr_timer_list_t *tl, fr_event_time_source_t func)
Override event list time source.
timer_list_deferred_t deferred
Function to process deferred events.
static fr_timer_t * timer_list_ordered_head(fr_timer_list_t *tl)
Return the head of the ordered list.
bool in_handler
Whether we're currently in a callback.
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
char const * file
Source file this event was last updated in.
fr_timer_list_t * tl
The event list this timer is part of.
static int timer_list_ordered_deferred(fr_timer_list_t *tl)
Move all deferred events into the ordered event list.
int(* timer_insert_t)(fr_timer_list_t *tl, fr_timer_t *ev)
Specialisation function to insert a timer.
timer_list_run_t run
Function to run a timer event.
int _fr_timer_at(NDEBUG_LOCATION_ARGS TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p, fr_time_t when, bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
Insert a timer event into an event list.
#define FR_TIMER_DISARM_RETURN(_ev)
fr_time_t(* fr_event_time_source_t)(void)
Alternative time source, useful for testing.
void(* fr_timer_cb_t)(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Called when a timer event fires.
fr_event_time_source_t _CONST time
Time source this list uses to get the current time when calculating deltas (fr_timer_in).
Public event timer list structure.
char const * fr_strerror(void)
Get the last library error.
char const * fr_strerror_peek(void)
Get the last library error.
#define fr_strerror_const_push(_msg)
#define fr_strerror_const(_msg)