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>
76#ifdef WITH_EVENT_DEBUG
114#define CHECK_PARENT(_ev) \
115 fr_assert_msg(!(_ev)->parent || (*(_ev)->parent == ev), \
116 "Event %p, allocd %s[%d], parent field points to %p", (_ev), (_ev)->file, (_ev)->line, *(_ev)->parent);
118#define TIMER_UCTX_TO_TIME(_tl, _x) ((fr_time_t *)(((uintptr_t) (_x)) + (_tl)->shared.time_offset))
185#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
290 if (!tl->
parent)
return 0;
367 tail = timer_tail(&tl->ordered);
373 if (
unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
394 if (ret < 0)
return ret;
466 if (
unlikely(ev->linked_ctx != ctx)) {
477 char const *err_file;
489 err_file =
"not-available";
497 "Event %p, allocd %s[%d], was not found in the event "
498 "list or deferred list when re-armed: %s", ev,
536 talloc_set_destructor(ev, NULL);
544 "Failed inserting event into deferred list")) {
551 if (
unlikely(ret < 0))
goto insert_failed;
590 free_on_fire, callback, uctx);
597 if (timer_in_list(&tl->
deferred,ev)) {
598 (void)timer_remove(&tl->
deferred, ev);
601 char const *err_file;
608 err_file =
"not-available";
617 "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
618 "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
639 if (!ev->
tl)
return 0;
646 (void)timer_remove(&ev->
tl->ordered, ev);
660 EVENT_DEBUG(
"Asked to disarm inactive timer %p (noop)", ev);
674 if (timer_in_list(&tl->
deferred,ev)) {
675 (void)timer_remove(&tl->
deferred, ev);
678 if (ret < 0)
return ret;
706 if (likely(ret == 0)) {
778 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
795 callback(tl, *when, uctx);
822 unsigned int fired = 0;
824 while ((ev = timer_head(&tl->ordered))) {
837 memcpy(&uctx, &ev->
uctx,
sizeof(uctx));
854 callback(tl, *when, uctx);
879 unsigned int fired = 0;
881 while ((uctx =
fr_rb_first(tl->shared.rb)) != NULL) {
897 tl->shared.callback(tl, *when, uctx);
956 if (timer_num_elements(&tl->
deferred) > 0) {
992 return timer_head(&tl->ordered);
1007 while((ev = timer_pop_head(&tl->
deferred))) {
1009 timer_insert_head(&tl->
deferred, ev);
1034 tail = timer_tail(&tl->ordered);
1042 "Deferred event is earlier than the last event in the ordered list");
1050 while ((ev = timer_pop_head((&tl->
deferred)))) {
1051 timer_insert_tail(&tl->ordered, ev);
1070 (
fr_rb_node_t *) (((uintptr_t) (uctx)) + tl->shared.node_offset));
1086 return timer_num_elements(&tl->ordered);
1153 return num + timer_num_elements(&tl->
deferred);
1163 if (ev)
return &ev->
when;
1190 if (when)
return *when;
1275#ifdef WITH_EVENT_REPORT
1318 tl->shared.node_offset = node_offset;
1319 tl->shared.callback = callback;
1321 tl->shared.rb =
_fr_rb_alloc(tl, node_offset, NULL, cmp, NULL);
1322 if (!tl->shared.rb) {
1372 (
fr_rb_node_t *) (((uintptr_t) (uctx)) + tl->shared.node_offset));
1385#if defined(WITH_EVENT_DEBUG) && !defined(NDEBUG)
1387 { 1 }, { 10 }, { 100 },
1388 { 1000 }, { 10000 }, { 100000 },
1389 { 1000000 }, { 10000000 }, { 100000000 },
1390 { 1000000000 }, { 10000000000 }, { 100000000000 },
1391 { 1000000000000 }, { 10000000000000 }, { 100000000000000 },
1392 { 1000000000000000 }, { 10000000000000000 }, { 100000000000000000 },
1395static const char *decade_names[18] = {
1396 "1ns",
"10ns",
"100ns",
1397 "1us",
"10us",
"100us",
1398 "1ms",
"10ms",
"100ms",
1399 "1s",
"10s",
"100s",
1400 "1Ks",
"10Ks",
"100Ks",
1401 "1Ms",
"10Ms",
"100Ms",
1409} fr_event_counter_t;
1411static int8_t timer_location_cmp(
void const *one,
void const *two)
1413 fr_event_counter_t
const *a = one;
1414 fr_event_counter_t
const *b = two;
1418 return CMP(a->line, b->line);
1428 fr_event_counter_t find = { .file = ev->
file, .line = ev->
line };
1429 fr_event_counter_t *counter;
1433 counter = talloc(locations[i], fr_event_counter_t);
1438 counter->file = ev->
file;
1439 counter->line = ev->
line;
1465 TALLOC_CTX *tmp_ctx;
1469 EVENT_DEBUG(
"Cannot (yet) do timer report for TIMER_LIST_TYPE_SHARED");
1482 locations[i] =
fr_rb_inline_alloc(tmp_ctx, fr_event_counter_t, node, timer_location_cmp, NULL);
1483 if (!locations[i])
goto oom;
1495 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1504 for (ev = timer_head(&tl->ordered);
1506 ev = timer_next(&tl->ordered, ev)) {
1507 if (_event_report_process(locations, array, now, ev) < 0)
goto oom;
1516 pthread_mutex_lock(&print_lock);
1523 if (!array[i])
continue;
1526 EVENT_DEBUG(
" events <= %5s : %zu", decade_names[i], array[i]);
1528 EVENT_DEBUG(
" events > %5s : %zu", decade_names[i - 1], array[i]);
1530 EVENT_DEBUG(
" events %5s - %5s : %zu", decade_names[i - 1], decade_names[i], array[i]);
1536 fr_event_counter_t *counter = talloc_get_type_abort(node, fr_event_counter_t);
1539 counter->count, counter->file, counter->line);
1542 pthread_mutex_unlock(&print_lock);
1554#define TIMER_DUMP(_ev) \
1555 EVENT_DEBUG("%s[%d]: %p time=%" PRId64 " (%c), callback=%p", \
1556 (_ev)->file, (_ev)->line, _ev, fr_time_unwrap((_ev)->when), \
1557 fr_time_gt(now, (_ev)->when) ? '<' : '>', (_ev)->callback);
1576 for (ev = timer_head(&tl->ordered);
1578 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.
static fr_ring_buffer_t * rb
#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
int8_t(* fr_cmp_t)(void const *a, void const *b)
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
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_remove(fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
fr_rb_tree_t * _fr_rb_alloc(TALLOC_CTX *ctx, ssize_t offset, char const *type, fr_cmp_t data_cmp, fr_free_t data_free)
Alloc a new RED-BLACK tree.
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
void * fr_rb_remove_by_inline_node(fr_rb_tree_t *tree, fr_rb_node_t *node)
Remove an entry from the tree, using the node structure, without freeing the data.
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.
void * fr_rb_first(fr_rb_tree_t *tree)
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.
#define fr_rb_inorder_foreach(_tree, _type, _iter)
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_delta_wrap(_time)
#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)
Disarm a timer 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)
Move all deferred events into 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.
int fr_timer_uctx_insert(fr_timer_list_t *tl, void *uctx)
Insert a uctx into a shared timer, and update the timer.
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.
fr_time_delta_t fr_timer_remaining(fr_timer_t *ev)
Return time delta between now and when the timer should fire.
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)
Forcibly run all events in an event loop.
fr_timer_cb_t callback
Callback to execute when the timer fires.
static int timer_lst_disarm(fr_timer_t *ev)
bool disarmed
the entire timer list is disarmed
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.
static int timer_list_shared_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled events in an ordered list.
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.
static int timer_list_shared_deferred(fr_timer_list_t *tl)
Move all deferred events into the shared list.
fr_timer_list_t * fr_timer_list_shared_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent, fr_cmp_t cmp, fr_timer_cb_t callback, size_t node_offset, size_t time_offset)
Allocate a new shared event timer list.
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 fr_time_t * timer_list_when(fr_timer_list_t *tl)
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 lst.
static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl)
int fr_timer_uctx_remove(fr_timer_list_t *tl, void *uctx)
Remove a uctx from a shared timer.
timer_list_type_t
What type of event list the timer is inserted into.
@ TIMER_LIST_TYPE_SHARED
all events share one event callback
@ 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.
void * fr_timer_uctx_peek(fr_timer_list_t *tl)
static fr_timer_t * timer_list_ordered_head(fr_timer_list_t *tl)
Return the head of the ordered list.
#define TIMER_UCTX_TO_TIME(_tl, _x)
int fr_timer_list_arm(fr_timer_list_t *tl)
Arm (or re-arm) a timer 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.
static uint64_t timer_list_shared_num_events(fr_timer_list_t *tl)
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.
static bool fr_timer_armed(fr_timer_t *ev)
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.
fr_time_delta_t time_offset
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)