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/strerror.h>
32#include <freeradius-devel/util/timer.h>
33#include <freeradius-devel/util/value.h>
34#include <freeradius-devel/util/lst.h>
35#include <freeradius-devel/util/rb.h>
71#ifdef WITH_EVENT_DEBUG
109#define CHECK_PARENT(_ev) \
110 fr_assert_msg(!(_ev)->parent || (*(_ev)->parent == ev), \
111 "Event %p, allocd %s[%d], parent field points to %p", (_ev), (_ev)->file, (_ev)->line, *(_ev)->parent);
178#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
264 if (!tl->
parent)
return 0;
320 tail = timer_tail(&tl->ordered);
326 if (
unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
347 if (ret < 0)
return ret;
417 if (
unlikely(ev->linked_ctx != ctx)) {
428 char const *err_file;
440 err_file =
"not-available";
448 "Event %p, allocd %s[%d], was not found in the event "
449 "list or deferred list when re-armed: %s", ev,
487 talloc_set_destructor(ev, NULL);
495 "Failed inserting event into deferred list")) {
502 if (
unlikely(ret < 0))
goto insert_failed;
541 free_on_fire, callback, uctx);
548 if (timer_in_list(&tl->
deferred,ev)) {
549 (void)timer_remove(&tl->
deferred, ev);
552 char const *err_file;
559 err_file =
"not-available";
568 "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
569 "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
590 if (!ev->
tl)
return 0;
597 (void)timer_remove(&ev->
tl->ordered, ev);
611 EVENT_DEBUG(
"Asked to disarm inactive timer %p (noop)", ev);
625 if (timer_in_list(&tl->
deferred,ev)) {
626 (void)timer_remove(&tl->
deferred, ev);
629 if (ret < 0)
return ret;
657 if (likely(ret == 0)) {
718 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
735 callback(tl, *when, uctx);
762 unsigned int fired = 0;
764 while ((ev = timer_head(&tl->ordered))) {
777 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
794 callback(tl, *when, uctx);
833 if (timer_num_elements(&tl->
deferred) > 0) {
869 return timer_head(&tl->ordered);
883 while((ev = timer_pop_head(&tl->
deferred))) {
885 timer_insert_head(&tl->
deferred, ev);
910 tail = timer_tail(&tl->ordered);
918 "Deferred event is earlier than the last event in the ordered list");
926 while ((ev = timer_pop_head((&tl->
deferred)))) {
927 timer_insert_tail(&tl->ordered, ev);
940 return timer_num_elements(&tl->ordered);
969 return num + timer_num_elements(&tl->
deferred);
983 if (ev)
return ev->
when;
1064#ifdef WITH_EVENT_REPORT
1088#if defined(WITH_EVENT_DEBUG) && !defined(NDEBUG)
1090 { 1 }, { 10 }, { 100 },
1091 { 1000 }, { 10000 }, { 100000 },
1092 { 1000000 }, { 10000000 }, { 100000000 },
1093 { 1000000000 }, { 10000000000 }, { 100000000000 },
1094 { 1000000000000 }, { 10000000000000 }, { 100000000000000 },
1095 { 1000000000000000 }, { 10000000000000000 }, { 100000000000000000 },
1098static const char *decade_names[18] = {
1099 "1ns",
"10ns",
"100ns",
1100 "1us",
"10us",
"100us",
1101 "1ms",
"10ms",
"100ms",
1102 "1s",
"10s",
"100s",
1103 "1Ks",
"10Ks",
"100Ks",
1104 "1Ms",
"10Ms",
"100Ms",
1112} fr_event_counter_t;
1114static int8_t timer_location_cmp(
void const *one,
void const *two)
1116 fr_event_counter_t
const *a = one;
1117 fr_event_counter_t
const *b = two;
1121 return CMP(a->line, b->line);
1131 fr_event_counter_t find = { .file = ev->
file, .line = ev->
line };
1132 fr_event_counter_t *counter;
1136 counter = talloc(locations[i], fr_event_counter_t);
1141 counter->file = ev->
file;
1142 counter->line = ev->
line;
1168 TALLOC_CTX *tmp_ctx;
1180 locations[i] =
fr_rb_inline_alloc(tmp_ctx, fr_event_counter_t, node, timer_location_cmp, NULL);
1181 if (!locations[i])
goto oom;
1193 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1202 for (ev = timer_head(&tl->ordered);
1204 ev = timer_next(&tl->ordered, ev)) {
1205 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1210 pthread_mutex_lock(&print_lock);
1217 if (!array[i])
continue;
1220 EVENT_DEBUG(
" events <= %5s : %zu", decade_names[i], array[i]);
1222 EVENT_DEBUG(
" events > %5s : %zu", decade_names[i - 1], array[i]);
1224 EVENT_DEBUG(
" events %5s - %5s : %zu", decade_names[i - 1], decade_names[i], array[i]);
1230 fr_event_counter_t *counter = talloc_get_type_abort(node, fr_event_counter_t);
1233 counter->count, counter->file, counter->line);
1236 pthread_mutex_unlock(&print_lock);
1248#define TIMER_DUMP(_ev) \
1249 EVENT_DEBUG("%s[%d]: %p time=%" PRId64 " (%c), callback=%p", \
1250 (_ev)->file, (_ev)->line, _ev, fr_time_unwrap((_ev)->when), \
1251 fr_time_gt(now, (_ev)->when) ? '<' : '>', (_ev)->callback);
1270 for (ev = timer_head(&tl->ordered);
1272 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.
Functions which we wish were included in the standard talloc distribution.
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.
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.
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.
bool fr_timer_armed(fr_timer_t *ev)
Check if a timer event is armed.
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)