The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
timer.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Various types of event timer list
18 *
19 * @file src/lib/util/timer.c
20 *
21 * @copyright 2025 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
22 */
23
24#define _TIMER_PRIVATE 1
26
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
34FR_DLIST_TYPES(timer)
35FR_DLIST_TYPEDEFS(timer, fr_timer_head_t, fr_timer_entry_t)
36
37/** What type of event list the timer is inserted into
38 *
39 */
40typedef enum : uint8_t {
41 TIMER_LIST_TYPE_LST = 1, //!< Self-sorting timer list based on a left leaning skeleton tree.
42 TIMER_LIST_TYPE_ORDERED = 2, //!< Strictly ordered list of events in a dlist.
43 TIMER_LIST_TYPE_SHARED = 3 //!< all events share one event callback
45
46/** An event timer list
47 *
48 */
50 struct fr_timer_list_pub_s pub; //!< Public interface to the event timer list.
51
52 union {
53 fr_lst_t *lst; //!< of timer events to be executed.
54 timer_head_t ordered; //!< A list of timer events to be executed.
55 struct {
56 fr_rb_tree_t *rb; //!< a tree of raw pointers
57 fr_rb_tree_t *deferred; //!< a tree of deferred things
58 size_t time_offset; //!< offset from uctx to the fr_time_t it contains
59 size_t node_offset; //!< offset from uctx to the fr_rb_node it contains
60 fr_timer_cb_t callback; //!< the callback to run
61 } shared;
62 };
64 bool in_handler; //!< Whether we're currently in a callback.
65 bool disarmed; //!< the entire timer list is disarmed
66
67 timer_head_t deferred; //!< A list of timer events to be inserted, after
68 ///< the current batch has been processed.
69 ///< This prevents "busy" timer loops, where
70 ///< other events may starve, or we may never exit.
71
72 fr_timer_list_t *parent; //!< Parent list to insert event into (if any).
73 fr_timer_t *parent_ev; //!< Event in the parent's event loop.
74
75#ifdef WITH_EVENT_DEBUG
76 fr_timer_t *report; //!< Used to trigger periodic reports about the event timer list.
77#endif
78};
79
80/** A timer event
81 *
82 */
83struct fr_timer_s {
84 fr_time_t when; //!< When this timer should fire.
85
86 fr_timer_cb_t callback; //!< Callback to execute when the timer fires.
87 void const *uctx; //!< Context pointer to pass to the callback.
88
89 TALLOC_CTX *linked_ctx; //!< talloc ctx this event was bound to.
90
91 fr_timer_t **parent; //!< A pointer to the parent structure containing the timer
92 ///< event.
93
94 fr_timer_entry_t entry; //!< Entry in a list of timer events.
95 union {
96 fr_dlist_t ordered_entry; //!< Entry in an ordered list of timer events.
97 fr_lst_index_t lst_idx; //!< Where to store opaque lst data, not used for ordered lists.
98 };
99 bool free_on_fire; //!< Whether to free the event when it fires.
100
101 fr_timer_list_t *tl; //!< The event list this timer is part of.
102 ///< This is set to NULL when an event is disarmed,
103 ///< but all other fields are left intact.
104
105#ifndef NDEBUG
106 char const *file; //!< Source file this event was last updated in.
107 int line; //!< Line this event was last updated on.
108#endif
109};
110
111FR_DLIST_FUNCS(timer, fr_timer_t, entry)
112
113#define CHECK_PARENT(_ev) \
114 fr_assert_msg(!(_ev)->parent || (*(_ev)->parent == ev), \
115 "Event %p, allocd %s[%d], parent field points to %p", (_ev), (_ev)->file, (_ev)->line, *(_ev)->parent)
116
117#define TIMER_UCTX_TO_TIME(_tl, _x) ((fr_time_t *)(((uintptr_t) (_x)) + (_tl)->shared.time_offset))
118
119/*
120 * When looping over a timer list, stop if the entry in the list is in the future.
121 *
122 * OR stop if a callback disarmed the list. When the list is re-armed with fr_timer_list_arm(), that
123 * function will take care of running any pending timer events.
124 */
125#define TIMER_STOP_IF_DONE(_next) \
126 do { \
127 if (fr_time_gt(_next, *when) || tl->disarmed) { \
128 *when = _next; \
129 goto done; \
130 } \
131 } while (0)
132
133#define TIMER_RETURN_DONE \
134 do { \
135 *when = fr_time_wrap(0); \
136 done: \
137 return fired; \
138 } while (0)
139
140/** Specialisation function to insert a timer
141 *
142 * @param[in] tl Timer list to insert into.
143 * @param[in] ev Timer event to insert.
144 * @return
145 * - 0 on success.
146 * - -1 on failure.
147 */
149
150/** Specialisation function to delete a timer
151 *
152 * @param[in] ev Timer event to delete.
153 * @return
154 * - 0 on success.
155 * - -1 on failure.
156 */
157typedef int (*timer_disarm_t)(fr_timer_t *ev);
158
159/** Specialisation function to execute any pending timers
160 *
161 * @param[in] tl Timer list to execute.
162 * @param[in,out] when Our current time, updated to the next event time (i.e. the next time we'll need to run something)
163 * @return
164 * - 0 no timer events fired.
165 * - 1 a timer event fired.
166 */
167typedef int (*timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when);
168
169/** Return the soonest timer event
170 *
171 * @param[in] tl to get the head of.
172 * @return
173 * - The head of the list.
174 * - NULL if the list is empty.
175 */
176typedef fr_timer_t *(*timer_list_head_t)(fr_timer_list_t *tl);
177
178/** Process any deferred timer events
179 *
180 * @param[in] tl to process deferred events for.
181 * @return
182 * - The head of the list.
183 * - NULL if the list is empty.
184 */
186
187/** Return the number of elements in the list
188 *
189 * @param[in] tl to get the number of elements from.
190 * @return
191 * - The number of elements in the list.
192 */
194
195typedef struct {
196 timer_insert_t insert; //!< Function to insert a timer event.
197 timer_disarm_t disarm; //!< Function to delete a timer event.
198
199 timer_list_run_t run; //!< Function to run a timer event.
200 timer_list_head_t head; //!< Function to get the head of the list.
201 timer_list_deferred_t deferred; //!< Function to process deferred events.
202 timer_list_num_elements_t num_events; //!< Function to get the number of elements in the list.
204
205#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
206
208
211
212static int timer_lst_disarm(fr_timer_t *ev);
213static int timer_ordered_disarm(fr_timer_t *ev);
214
215static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when);
217;static int timer_list_shared_run(fr_timer_list_t *tl, fr_time_t *when);
218
221
225
226static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl);
229
230/** Functions for performing operations on various types of timer list
231 *
232 */
236 .disarm = timer_lst_disarm,
237
238 .run = timer_list_lst_run,
239 .head = timer_list_lst_head,
240 .deferred = timer_list_lst_deferred,
241 .num_events = timer_list_lst_num_events
242 },
244 .insert = timer_ordered_insert_at,
245 .disarm = timer_ordered_disarm,
246
249 .deferred = timer_list_ordered_deferred,
251 },
253// .insert = timer_shared_insert_at,
254// .disarm = timer_shared_disarm,
255
257// .head = timer_list_shared_head,
258 .deferred = timer_list_shared_deferred,
259 .num_events = timer_list_shared_num_events
260 },
261};
262
263/** Compare two timer events to see which one should occur first
264 *
265 * @param[in] a the first timer event.
266 * @param[in] b the second timer event.
267 * @return
268 * - +1 if a should occur later than b.
269 * - -1 if a should occur earlier than b.
270 * - 0 if both events occur at the same time.
271 */
272static fr_cmp_ret_t timer_cmp(void const *a, void const *b)
273{
274 fr_timer_t const *ev_a = a, *ev_b = b;
275
276 return fr_time_cmp(ev_a->when, ev_b->when);
277}
278
279
280/** This callback fires in the parent to execute events in this sublist
281 *
282 * @param[in] parent_tl Parent event timer list.
283 * @param[in] when When the parent timer fired.
284 * @param[in] uctx Sublist to execute.
285 */
286static void _parent_timer_cb(UNUSED fr_timer_list_t *parent_tl, fr_time_t when, void *uctx)
287{
288 fr_timer_list_t *tl = talloc_get_type_abort(uctx, fr_timer_list_t);
289
290 fr_assert(!tl->disarmed);
291
292 /*
293 * We're in the parent timer, so we need to run the
294 * events in the child timer list.
295 */
296 (void)fr_timer_list_run(tl, &when);
297}
298
299/** Utility function to update parent timers
300 *
301 * @param[in] tl to update parent timers for.
302 * @return
303 * - 0 on success.
304 * - -1 on failure.
305 */
306static inline CC_HINT(always_inline) int timer_list_parent_update(fr_timer_list_t *tl)
307{
308 fr_time_t *when;
309
310 if (!tl->parent) return 0;
311
312 when = timer_list_when(tl);
313
314 /*
315 * No events, disarm the timer
316 */
317 if (!when) {
318 /*
319 * Disables the timer in the parent, does not free the memory
320 */
322 return 0;
323 }
324
325 /*
326 * We have an active event, we can suppress changes which have no effect.
327 */
328 if (tl->parent_ev && EVENT_ARMED(tl->parent_ev)) {
329 fr_assert(!tl->disarmed); /* fr_timer_list_disarm() must disarm it */
330
331 if (fr_time_eq(*when, tl->parent_ev->when)) {
332 return 0;
333 }
334 }
335
336 /*
337 * This is a child list which is disabled. Don't update the parent.
338 */
339 if (tl->disarmed) {
340 fr_assert(tl->parent);
341
343 return 0;
344 }
345
346 /*
347 * The list is armed and the head has changed, so we change the event in the parent list.
348 */
349 if (fr_timer_at(tl, tl->parent, &tl->parent_ev,
350 *when, false, _parent_timer_cb, tl) < 0) return -1;
351
352 return 0;
353}
354
355/** Insert a timer event into a single event timer list
356 *
357 * @param[in] tl to insert the event into.
358 * @param[in] ev to insert.
359 * @return
360 * - 0 on success.
361 * - -1 on failure.
362 */
364{
365 if (unlikely(fr_lst_insert(tl->lst, ev) < 0)) {
366 fr_strerror_const_push("Failed inserting timer into lst");
367 return -1;
368 }
369
370 return 0;
371}
372
373/** Insert an event into an ordered timer list
374 *
375 * Timer must be in order, i.e. either before first event, or after last event
376 *
377 * @param[in] tl to insert the event into.
378 * @param[in] ev to insert.
379 * @return
380 * - 0 on success.
381 * - -1 on failure.
382 */
384{
385 fr_timer_t *tail;
386
387 tail = timer_tail(&tl->ordered);
388 if (tail && fr_time_lt(ev->when, tail->when)) {
389 fr_strerror_const("Event being inserted must occurr _after_ the last event");
390 return -1;
391 }
392
393 if (unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
394 fr_strerror_const_push("Failed inserting timer into ordered list");
395 return -1;
396 }
397
398 return 0;
399}
400
401/** Remove an event from the event loop
402 *
403 * @param[in] ev to free.
404 * @return
405 * - 0 on success.
406 * - -1 on failure.
407 */
408static int _timer_free(fr_timer_t *ev)
409{
410 fr_timer_t **ev_p;
411 int ret;
412
413 ret = fr_timer_disarm(ev); /* Is a noop if ev->tl == NULL */
414 if (ret < 0) return ret;
415
416 CHECK_PARENT(ev);
417 ev_p = ev->parent;
418 *ev_p = NULL;
419
420 return 0;
421}
422
423/** Insert a timer event into an event list
424 *
425 * @note The talloc parent of the memory returned in ev_p must not be changed.
426 * If the lifetime of the event needs to be bound to another context
427 * this function should be called with the existing event pointed to by
428 * ev_p.
429 *
430 * @param[in] ctx to bind lifetime of the event to.
431 * @param[in] tl to insert event into.
432 * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
433 * in a temporal sense, not in a memory structure or dependency sense.
434 * @param[in] when we should run the event.
435 * @param[in] free_on_fire Whether event memory should be freed if the event fires.
436 * @param[in] callback function to execute if the event fires.
437 * @param[in] uctx user data to pass to the event.
438 * @return
439 * - 0 on success.
440 * - -1 on failure.
441 */
443 TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
444 fr_time_t when,
445 bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
446{
447 fr_timer_t *ev;
448
450
451 /*
452 * If there is an event, reuse it instead of freeing it
453 * and allocating a new one. This is to reduce memory
454 * churn for repeat events.
455 */
456 if (!*ev_p) {
457 new_event:
458 ev = talloc_zero(tl, fr_timer_t);
459 if (unlikely(!ev)) {
460 fr_strerror_const("Out of memory");
461 return -1;
462 }
463
464 EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Added new timer %p", tl, NDEBUG_LOCATION_VALS ev);
465 /*
466 * Bind the lifetime of the event to the specified
467 * talloc ctx. If the talloc ctx is freed, the
468 * event will also be freed.
469 */
470 if (ctx != tl) talloc_link_ctx(ctx, ev);
471
472 talloc_set_destructor(ev, _timer_free);
473 } else {
474 ev = talloc_get_type_abort(UNCONST(fr_timer_t *, *ev_p), fr_timer_t);
475
476 EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Re-armed timer %p", tl, NDEBUG_LOCATION_VALS ev);
477
478 /*
479 * We can't disarm the linking context due to
480 * limitations in talloc, so if the linking
481 * context changes, we need to free the old
482 * event, and allocate a new one.
483 *
484 * Freeing the event also removes it from the lst.
485 */
486 if (unlikely(ev->linked_ctx != ctx)) {
487 talloc_free(ev);
488 goto new_event;
489 }
490
491 /*
492 * If the event is associated with a list, we need
493 * to disarm it, before we can rearm it.
494 */
495 if (EVENT_ARMED(ev)) {
496 int ret;
497 char const *err_file;
498 int err_line;
499
500 /*
501 * Removed event from the event list or the
502 * deferred list.
503 */
504 ret = fr_timer_disarm(ev);
505#ifndef NDEBUG
506 err_file = ev->file;
507 err_line = ev->line;
508#else
509 err_file = "not-available";
510 err_line = 0;
511#endif
512
513 /*
514 * Events MUST be in the lst (or the insertion list).
515 */
516 if (!fr_cond_assert_msg(ret == 0,
517 "Event %p, allocd %s[%d], was not found in the event "
518 "list or deferred list when re-armed: %s", ev,
519 err_file, err_line, fr_strerror())) return -1;
520 }
521 }
522
523 ev->tl = tl; /* This indicates the event memory is bound to an event loop */
524 ev->when = when;
525 ev->free_on_fire = free_on_fire;
526 ev->callback = callback;
527 ev->uctx = uctx;
528 ev->linked_ctx = ctx;
529 ev->parent = ev_p;
530#ifndef NDEBUG
531 ev->file = file;
532 ev->line = line;
533#endif
534
535 /*
536 * No updating needed as the events are deferred
537 */
538 if (tl->in_handler) {
539 /*
540 * ...a little hacky, but we need to verify that
541 * we're not inserting an event that's earlier
542 * than the last event in the list for ordered
543 * lists.
544 *
545 * Otherwise we'd end up doing this when we tried
546 * to move all the deferred events into the timer
547 * list, and end up making that O(n) instead of O(1).
548 */
549 if (tl->type == TIMER_LIST_TYPE_ORDERED) {
551
552 if (head && fr_time_lt(ev->when, head->when)) {
553 fr_strerror_const("Event being inserted must occurr _after_ the last event");
554
555 insert_failed:
556 talloc_set_destructor(ev, NULL);
557 talloc_free(ev);
558 *ev_p = NULL;
559 return -1;
560 }
561 }
562
563 if (!fr_cond_assert_msg(timer_insert_tail(&tl->deferred, ev) == 0,
564 "Failed inserting event into deferred list")) {
565 goto insert_failed;
566 }
567 } else {
568 int ret;
569
570 ret = timer_funcs[tl->type].insert(tl, ev);
571 if (unlikely(ret < 0)) goto insert_failed;
572
573 /*
574 * We need to update the parent timer
575 * to ensure it fires at the correct time.
576 */
577 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
578 }
579
580 *ev_p = ev;
581
582 return 0;
583}
584
585/** Insert a timer event into an event list
586 *
587 * @note The talloc parent of the memory returned in ev_p must not be changed.
588 * If the lifetime of the event needs to be bound to another context
589 * this function should be called with the existing event pointed to by
590 * ev_p.
591 *
592 * @param[in] ctx to bind lifetime of the event to.
593 * @param[in] tl to insert event into.
594 * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
595 * in a temporal sense, not in a memory structure or dependency sense.
596 * @param[in] delta In how many nanoseconds to wait before should we execute the event.
597 * @param[in] free_on_fire Whether event memory should be freed if the event fires.
598 * @param[in] callback function to execute if the event fires.
599 * @param[in] uctx user data to pass to the event.
600 * @return
601 * - 0 on success.
602 * - -1 on failure.
603 */
605 TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
606 fr_time_delta_t delta,
607 bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
608{
610 ctx, tl, ev_p, fr_time_add(tl->pub.time(), delta),
611 free_on_fire, callback, uctx);
612}
613
615{
616 fr_timer_list_t *tl = ev->tl;
617
618 if (timer_in_list(&tl->deferred,ev)) {
619 (void)timer_remove(&tl->deferred, ev);
620 } else {
621 int ret = fr_lst_extract(tl->lst, ev);
622 char const *err_file;
623 int err_line;
624
625#ifndef NDEBUG
626 err_file = ev->file;
627 err_line = ev->line;
628#else
629 err_file = "not-available";
630 err_line = 0;
631#endif
632
633
634 /*
635 * Events MUST be in the lst (or the insertion list).
636 */
637 if (!fr_cond_assert_msg(ret == 0,
638 "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
639 "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
640 fr_strerror())) return -1;
641 }
642
643 return 0;
644}
645
646/** Remove a timer from a timer list, but don't free it
647 *
648 * @param[in] ev to remove.
649 */
651{
652 /*
653 * Check the check is still valid (sanity check)
654 */
655 (void)talloc_get_type_abort(ev, fr_timer_t);
656
657 /*
658 * Already dissassociated from a list, nothing to do.
659 */
660 if (!ev->tl) return 0;
661
662 /*
663 * This *MUST* be in a timer list if it has a non-NULL tl pointer.
664 */
665 if (unlikely(!fr_cond_assert(timer_in_list(&ev->tl->ordered, ev)))) return -1;
666
667 (void)timer_remove(&ev->tl->ordered, ev);
668
669 return 0;
670}
671
672/** Remove an event from the event list, but don't free the memory
673 *
674 * @param[in] ev to remove from the event list.
675 */
677{
678 fr_timer_list_t *tl;
679
680 if (!ev || !EVENT_ARMED(ev)) {
681 EVENT_DEBUG("Asked to disarm inactive timer %p (noop)", ev);
682 return 0; /* Noop */
683 }
684
685 tl = ev->tl;
686
687 EVENT_DEBUG("Disarming timer %p", ev);
688
689 CHECK_PARENT(ev);
690
691 /*
692 * If the event is deferred, it's not in the event list proper
693 * so just remove it, and set the tl pointer to NULL.
694 */
695 if (timer_in_list(&tl->deferred,ev)) {
696 (void)timer_remove(&tl->deferred, ev);
697 } else {
698 int ret = timer_funcs[ev->tl->type].disarm(ev);
699 if (ret < 0) return ret;
700 }
701 ev->tl = NULL;
702
703 return timer_list_parent_update(tl);
704}
705
706/** Delete a timer event and free its memory
707 *
708 * @param[in] ev_p of the event being deleted.
709 * @return
710 * - 0 on success.
711 * - -1 on failure.
712 */
714{
715 fr_timer_t *ev;
716 int ret;
717
718 if (unlikely(!*ev_p)) return 0;
719
720 ev = *ev_p;
721 ret = talloc_free(ev); /* Destructor removed event from any lists */
722
723 /*
724 * Don't leave a garbage pointer value
725 * if parent is not ev_p.
726 */
727 if (likely(ret == 0)) {
728 *ev_p = NULL;
729 return 0;
730 }
731
732 EVENT_DEBUG("Deleting timer %p failed: %s", ev, fr_strerror_peek());
733 return -1;
734}
735
736/** Internal timestamp representing when the timer should fire
737 *
738 * @return When the timestamp should fire.
739 */
741{
742 if (!fr_timer_armed(ev)) return fr_time_wrap(0);
743 return ev->when;
744}
745
746/** Return time delta between now and when the timer should fire
747 *
748 * @param[in] ev to get the time delta for.
749 */
751{
752 if (!fr_timer_armed(ev)) return fr_time_delta_wrap(0);
753 return fr_time_sub(ev->when, ev->tl->pub.time());
754}
755
756/** Check if a timer event is armed
757 *
758 * @param[in] ev to check.
759 * @return
760 * - true if the event is armed.
761 * - false if the event is not armed.
762 */
764{
765 return EVENT_ARMED(ev);
766}
767
768/** Run all scheduled timer events in a lst
769 *
770 * @param[in] tl containing the timer events.
771 * @param[in] when Process events scheduled to run before or at this time.
772 * - Set to 0 if no more events.
773 * - Set to the next event time if there are more events.
774 * @return
775 * - 0 no timer events fired.
776 * - 1 a timer event fired.
777 */
778CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
780{
781 fr_timer_cb_t callback;
782 void *uctx;
783 void *item;
784 fr_timer_t *ev;
785 int fired = 0;
786
787 while (fr_lst_num_elements(tl->lst) > 0) {
788 fr_lst_peek(&item, tl->lst);
789 ev = talloc_get_type_abort(item, fr_timer_t);
790
792
793 callback = ev->callback;
794 memcpy(&uctx, &ev->uctx, sizeof(uctx));
795
796 CHECK_PARENT(ev);
797
798 /*
799 * Disarm the event before calling it.
800 *
801 * This leaves the memory in place,
802 * but dissassociates it from the list.
803 *
804 * We use the public function as it
805 * handles more cases.
806 */
807 if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
808 EVENT_DEBUG("Running timer %p", ev);
809 if (ev->free_on_fire) talloc_free(ev);
810
811 callback(tl, *when, uctx);
812
813 fired++;
814 }
815
817}
818
819/** Run all scheduled events in an ordered list
820 *
821 * @param[in] tl containing the timer events.
822 * @param[in] when Process events scheduled to run before or at this time.
823 * - Set to 0 if no more events.
824 * - Set to the next event time if there are more events.
825 * @return
826 * - < 0 if we failed to updated the parent list.
827 * - 0 no timer events fired.
828 * - >0 number of timer event fired.
829 */
830CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
832{
833 fr_timer_cb_t callback;
834 void *uctx;
835 fr_timer_t *ev;
836 unsigned int fired = 0;
837
838 while ((ev = timer_head(&tl->ordered))) {
839 (void)talloc_get_type_abort(ev, fr_timer_t);
840
842
843 callback = ev->callback;
844 memcpy(&uctx, &ev->uctx, sizeof(uctx));
845
846 CHECK_PARENT(ev);
847
848 /*
849 * Disarm the event before calling it.
850 *
851 * This leaves the memory in place,
852 * but dissassociates it from the list.
853 *
854 * We use the public function as it
855 * handles more cases.
856 */
857 if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
858 EVENT_DEBUG("Running timer %p", ev);
859 if (ev->free_on_fire) talloc_free(ev);
860
861 callback(tl, *when, uctx);
862
863 fired++;
864 }
865
867}
868
869/** Run all scheduled events in an ordered list
870 *
871 * @param[in] tl containing the timer events.
872 * @param[in] when Process events scheduled to run before or at this time.
873 * - Set to 0 if no more events.
874 * - Set to the next event time if there are more events.
875 * @return
876 * - < 0 if we failed to updated the parent list.
877 * - 0 no timer events fired.
878 * - >0 number of timer event fired.
879 */
880CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
882{
883 void *uctx;
884 unsigned int fired = 0;
885
886 while ((uctx = fr_rb_first(tl->shared.rb)) != NULL) {
887 fr_time_t const *next;
888
889 next = TIMER_UCTX_TO_TIME(tl, uctx);
890
891 TIMER_STOP_IF_DONE(*next);
892
893 fr_rb_remove(NULL, tl->shared.rb, uctx);
894
895 tl->shared.callback(tl, *when, uctx);
896
897 fired++;
898 }
899
901}
902
903
904/** Forcibly run all events in an event loop.
905 *
906 * This is used to forcefully run every event in the event loop.
907 *
908 * We pass in the real time, which may theoretically cause issues if timer
909 * callbacks are checking... But the uses of this function are very limited.
910 *
911 * @return
912 * - < 0 if we failed to update the parent list.
913 * - 0 no timer events fired.
914 * - > 0 number of timer event fired.
915 */
917{
918 fr_time_t when = fr_time_max();
919
920 return fr_timer_list_run(tl, &when);
921}
922
923/** Execute any pending events in the event loop
924 *
925 * @param[in] tl to execute events in.
926 * @param[in] when Process events scheduled to run before or at this time.
927 * - Set to 0 if no more events.
928 * - Set to the next event time if there are more events.
929 * @return
930 * - < 0 if we failed to update the parent list.
931 * - 0 no timer events fired.
932 * - >0 number of timer event fired.
933 */
935{
936 int ret;
937 bool in_handler = tl->in_handler; /* allow nested timer execution */
938
939 tl->in_handler = true;
940 ret = timer_funcs[tl->type].run(tl, when);
941 tl->in_handler = in_handler;
942
943 /*
944 * Now we've executed all the pending events,
945 * now merge the deferred events into the main
946 * event list.
947 *
948 * The events don't need to be modified as they
949 * were initialised completely before being
950 * placed in the deferred list.
951 */
952 if (timer_num_elements(&tl->deferred) > 0) {
953 if (unlikely(timer_funcs[tl->type].deferred(tl) < 0)) return -1;
954 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
955 /*
956 * We ran some events, and have no deferred
957 * events to insert, so we need to forcefully
958 * update the parent timer.
959 */
960 } else if(ret > 0) {
961 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
962 }
963
964 return ret;
965}
966
967/** Return the head of the lst
968 *
969 * @param[in] tl to get the head of.
970 * @return
971 * - The head of the trie.
972 * - NULL, if there's no head.
973 */
975{
976 fr_timer_t *ev;
977
978 fr_lst_peek((void **)&ev, tl->lst);
979
980 return ev;
981}
982
983/** Return the head of the ordered list
984 *
985 * @param[in] tl to get the head of.
986 * @return
987 * - The head of the trie.
988 * - NULL, if there's no head.
989 */
991{
992 return timer_head(&tl->ordered);
993}
994
995
996/** Move all deferred events into the lst
997 *
998 * @param[in] tl to move events in.
999 * @return
1000 * - 0 on success.
1001 * - -1 on failure.
1002 */
1004{
1005 fr_timer_t *ev;
1006
1007 while((ev = timer_pop_head(&tl->deferred))) {
1008 if (unlikely(timer_lst_insert_at(tl, ev) < 0)) {
1009 timer_insert_head(&tl->deferred, ev); /* Don't lose track of events we failed to insert */
1010 return -1;
1011 }
1012 }
1013
1014 return 0;
1015}
1016
1017/** Move all deferred events into the ordered event list
1018 *
1019 * This operation is O(1).
1020 *
1021 * @param[in] tl to move events in.
1022 * @return
1023 * - 0 on success.
1024 * - -1 on failure.
1025 */
1027{
1028 fr_timer_t *ev;
1029#ifndef NDEBUG
1030 {
1031 fr_timer_t *head, *tail;
1032
1033 head = timer_head(&tl->deferred);
1034 tail = timer_tail(&tl->ordered);
1035
1036 /*
1037 * Something has gone catastrophically wrong if the
1038 * deferred event is earlier than the last event in
1039 * the ordered list, given all the checks we do.
1040 */
1041 fr_cond_assert_msg(!head || !tail || fr_time_gteq(head->when, tail->when),
1042 "Deferred event is earlier than the last event in the ordered list");
1043 }
1044#endif
1045
1046 /*
1047 * Can't use timer_move_head as entry positions
1048 * for the two lists are different.
1049 */
1050 while ((ev = timer_pop_head((&tl->deferred)))) {
1051 timer_insert_tail(&tl->ordered, ev);
1052 }
1053
1054 return 0;
1055}
1056
1057/** Move all deferred events into the shared list
1058 *
1059 * @param[in] tl to move events in.
1060 * @return
1061 * - 0 on success.
1062 * - -1 on failure.
1063 */
1065{
1066 void *uctx;
1067
1068 while((uctx = fr_rb_first(tl->shared.deferred)) != NULL) {
1070 (fr_rb_node_t *) (((uintptr_t) (uctx)) + tl->shared.node_offset));
1071
1072 fr_rb_insert(tl->shared.rb, uctx);
1073 }
1074
1075 return 0;
1076}
1077
1079{
1080 return fr_lst_num_elements(tl->lst);
1081}
1082
1084{
1085 return timer_num_elements(&tl->ordered);
1086}
1087
1089{
1090 return fr_rb_num_elements(tl->shared.rb);
1091}
1092
1093/** Disarm a timer list
1094 *
1095 * @param[in] tl Timer list to disarm
1096 * @return
1097 * - 0 on success.
1098 * - -1 on failure.
1099 */
1101{
1102 if (!tl->parent) {
1103 fr_strerror_const("Timer list does not have a parent");
1104 return -1;
1105 }
1106
1107 tl->disarmed = true;
1108
1110
1111 return 0;
1112}
1113
1114/** Arm (or re-arm) a timer list
1115 *
1116 * @param[in] tl Timer list to (re)-arm
1117 * @return
1118 * - 0 on success.
1119 * - -1 on failure.
1120 */
1122{
1123 if (!tl->parent) {
1124 fr_strerror_const("Timer list does not have a parent");
1125 return -1;
1126 }
1127
1128 if (!tl->disarmed) return 0;
1129
1130 tl->disarmed = false;
1131
1132 /*
1133 * Run any timer events which were missed during the time that the list was disarmed.
1134 */
1135 _parent_timer_cb(tl->parent, fr_time(), tl);
1136
1137 return timer_list_parent_update(tl);
1138}
1139
1140/** Return number of pending events
1141 *
1142 * @note This includes deferred events, i.e. those yet to be inserted into the main list
1143 *
1144 * @param[in] tl to get the number of events from.
1145 * @return
1146 * - The number of events in the list.
1147 */
1149{
1150 uint64_t num = timer_funcs[tl->type].num_events(tl);
1151
1152 return num + timer_num_elements(&tl->deferred);
1153}
1154
1156{
1157 fr_timer_t *ev;
1158
1159 switch (tl->type) {
1160 default:
1161 ev = timer_funcs[tl->type].head(tl);
1162 if (ev) return &ev->when;
1163 break;
1164
1166 void *uctx;
1167
1168 uctx = fr_rb_first(tl->shared.rb);
1169 if (!uctx) break;
1170
1171 return TIMER_UCTX_TO_TIME(tl, uctx);
1172 }
1173 }
1174
1175 return NULL;
1176}
1177
1178/** Return the time of the next event
1179 *
1180 * @param[in] tl to get the next event time from.
1181 * @return
1182 * - >0 the time of the next event.
1183 * - 0 if there are no more events.
1184 */
1186{
1187 fr_time_t const *when = timer_list_when(tl);
1188
1189 if (when) return *when;
1190
1191 return fr_time_wrap(0);
1192}
1193
1194/** Override event list time source
1195 *
1196 * @param[in] tl to set new time function for.
1197 * @param[in] func to set.
1198 */
1203
1204/** Cleanup all timers currently in the list
1205 *
1206 * @param[in] tl to cleanup.
1207 * @return
1208 * - 0 on success.
1209 * - -1 on failure.
1210 */
1212{
1213 fr_timer_t *ev;
1214
1215 if (unlikely(tl->in_handler)) {
1216 fr_strerror_const("Cannot free event timer list while in handler");
1217 return -1;
1218 }
1219
1220 if (tl->parent_ev) if (unlikely(fr_timer_delete(&tl->parent_ev) < 0)) return -1;
1221
1222 if (tl->type == TIMER_LIST_TYPE_SHARED) return 0;
1223
1224 while ((ev = timer_funcs[tl->type].head(tl))) {
1225 if (talloc_free(ev) < 0) return -1;
1226 }
1227
1228 return 0;
1229}
1230
1232{
1233 fr_timer_list_t *tl;
1234
1236
1237 tl = talloc_zero(ctx, fr_timer_list_t);
1238 if (unlikely(tl == NULL)) {
1239 fr_strerror_const("Out of memory");
1240 return NULL;
1241 }
1242
1243 timer_talloc_init(&tl->deferred);
1244 if (parent) {
1245 tl->parent = parent;
1246 tl->pub.time = parent->pub.time;
1247 } else {
1248 tl->pub.time = fr_time;
1249 }
1250 talloc_set_destructor(tl, _timer_list_free);
1251
1252 return tl;
1253}
1254
1255/** Allocate a new lst based timer list
1256 *
1257 * @note Entries may be inserted in any order.
1258 *
1259 * @param[in] ctx to insert head timer event into.
1260 * @param[in] parent to insert the head timer event into.
1261 */
1263{
1264 fr_timer_list_t *tl;
1265
1266 if (unlikely((tl = timer_list_alloc(ctx, parent)) == NULL)) return NULL;
1267
1268 tl->lst = fr_lst_talloc_alloc(tl, timer_cmp, fr_timer_t, lst_idx, 0);
1269 if (unlikely(tl->lst == NULL)) {
1270 fr_strerror_const("Failed allocating timer list");
1271 talloc_free(tl);
1272 return NULL;
1273 }
1275
1276#ifdef WITH_EVENT_REPORT
1277 fr_timer_in(tl, tl, &tl->report, fr_time_delta_from_sec(EVENT_REPORT_FREQ), false, fr_timer_report, NULL);
1278#endif
1279
1280 return tl;
1281}
1282
1283/** Allocate a new sorted event timer list
1284 *
1285 * @note Entries must be inserted in the order that they will fire.
1286 *
1287 * @param[in] ctx to allocate the event timer list from.
1288 * @param[in] parent to insert the head timer event into.
1289 */
1291{
1292 fr_timer_list_t *tl;
1293
1294 if (unlikely((tl = timer_list_alloc(ctx, parent)) == NULL)) return NULL;
1295
1296 fr_dlist_talloc_init((fr_dlist_head_t *)&tl->ordered, fr_timer_t, ordered_entry);
1298
1299 return tl;
1300}
1301
1302/** Allocate a new shared event timer list
1303 *
1304 * @param[in] ctx to allocate the event timer list from.
1305 * @param[in] parent to insert the head timer event into.
1306 * @param[in] cmp comparison routine (smaller times are earlier)
1307 * @param[in] callback to run on timer event
1308 * @param[in] node_offset offset from uctx to the fr_rb_node_t it contains
1309 * @param[in] time_offset offset from uctx to the fr_time_t it contains
1310 */
1312 fr_timer_cb_t callback, size_t node_offset, size_t time_offset)
1313{
1314 fr_timer_list_t *tl;
1315
1316 if (unlikely((tl = timer_list_alloc(ctx, parent)) == NULL)) return NULL;
1317
1319
1320 tl->shared.time_offset = time_offset;
1321 tl->shared.node_offset = node_offset;
1322 tl->shared.callback = callback;
1323
1324 tl->shared.rb = _fr_rb_alloc(tl, node_offset, NULL, cmp, NULL);
1325 if (!tl->shared.rb) {
1326 talloc_free(tl);
1327 return NULL;
1328 }
1329
1330 tl->shared.deferred = _fr_rb_alloc(tl, node_offset, NULL, cmp, NULL);
1331 if (!tl->shared.deferred) {
1332 talloc_free(tl);
1333 return NULL;
1334 }
1335
1336 return tl;
1337}
1338
1339/** Insert a uctx into a shared timer, and update the timer.
1340 *
1341 * @param[in] tl Timer list to insert into.
1342 * @param[in] uctx to insert
1343 * @return
1344 * - 0 on success.
1345 * - -1 on failure.
1346 */
1348{
1350
1351 if (tl->in_handler) {
1352 if (fr_rb_insert(tl->shared.deferred, uctx) != 0) return -1;
1353
1354 return 0;
1355 }
1356
1357 if (fr_rb_insert(tl->shared.rb, uctx) != 0) return -1;
1358
1359 return timer_list_parent_update(tl);
1360}
1361
1362/** Remove a uctx from a shared timer
1363 *
1364 * @param[in] tl Timer list to insert into.
1365 * @param[in] uctx to remove
1366 * @return
1367 * - 0 uctx was successfully removed.
1368 * - -1 uctx was removed, but the parent timer was not updated
1369 */
1371{
1373
1374 fr_rb_remove_by_inline_node(tl->shared.rb,
1375 (fr_rb_node_t *) (((uintptr_t) (uctx)) + tl->shared.node_offset));
1376
1377 return timer_list_parent_update(tl);
1378}
1379
1381{
1383
1384 return fr_rb_first(tl->shared.rb);
1385}
1386
1387
1388#if defined(WITH_EVENT_DEBUG) && !defined(NDEBUG)
1389static const fr_time_delta_t decades[18] = {
1390 { 1 }, { 10 }, { 100 },
1391 { 1000 }, { 10000 }, { 100000 },
1392 { 1000000 }, { 10000000 }, { 100000000 },
1393 { 1000000000 }, { 10000000000 }, { 100000000000 },
1394 { 1000000000000 }, { 10000000000000 }, { 100000000000000 },
1395 { 1000000000000000 }, { 10000000000000000 }, { 100000000000000000 },
1396};
1397
1398static const char *decade_names[18] = {
1399 "1ns", "10ns", "100ns",
1400 "1us", "10us", "100us",
1401 "1ms", "10ms", "100ms",
1402 "1s", "10s", "100s",
1403 "1Ks", "10Ks", "100Ks",
1404 "1Ms", "10Ms", "100Ms", /* 1 year is 300Ms */
1405};
1406
1407typedef struct {
1408 fr_rb_node_t node;
1409 char const *file;
1410 int line;
1412} fr_event_counter_t;
1413
1414static fr_cmp_ret_t timer_location_cmp(void const *one, void const *two)
1415{
1416 fr_event_counter_t const *a = one;
1417 fr_event_counter_t const *b = two;
1418
1419 CMP_RETURN(a, b, file);
1420
1421 return CMP(a->line, b->line);
1422}
1423
1424static int _event_report_process(fr_rb_tree_t **locations, size_t array[], fr_time_t now, fr_timer_t *ev)
1425{
1426 fr_time_delta_t diff = fr_time_sub(ev->when, now);
1427 size_t i;
1428
1429 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1430 if ((fr_time_delta_cmp(diff, decades[i]) <= 0) || (i == NUM_ELEMENTS(decades) - 1)) {
1431 fr_event_counter_t find = { .file = ev->file, .line = ev->line };
1432 fr_event_counter_t *counter;
1433
1434 fr_rb_find((void **)&counter, locations[i], &find);
1435 if (!counter) {
1436 counter = talloc(locations[i], fr_event_counter_t);
1437 if (!counter) {
1438 EVENT_DEBUG("Can't do report, out of memory");
1439 return -1;
1440 }
1441 counter->file = ev->file;
1442 counter->line = ev->line;
1443 counter->count = 1;
1444 fr_rb_insert(locations[i], counter);
1445 } else {
1446 counter->count++;
1447 }
1448
1449 array[i]++;
1450 break;
1451 }
1452 }
1453
1454 return 0;
1455}
1456
1457/** Print out information about timer events in the event loop
1458 *
1459 */
1460void fr_timer_report(fr_timer_list_t *tl, fr_time_t now, void *uctx)
1461{
1462 fr_lst_iter_t iter;
1463 fr_timer_t *ev;
1464 size_t i;
1465
1466 size_t array[NUM_ELEMENTS(decades)] = { 0 };
1467 fr_rb_tree_t *locations[NUM_ELEMENTS(decades)];
1468 TALLOC_CTX *tmp_ctx;
1469 static pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER;
1470
1471 if (tl->type == TIMER_LIST_TYPE_SHARED) {
1472 EVENT_DEBUG("Cannot (yet) do timer report for TIMER_LIST_TYPE_SHARED");
1473 return;
1474 }
1475
1476 tmp_ctx = talloc_init_const("temporary stats");
1477 if (!tmp_ctx) {
1478 oom:
1479 EVENT_DEBUG("Can't do report, out of memory");
1480 talloc_free(tmp_ctx);
1481 return;
1482 }
1483
1484 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1485 locations[i] = fr_rb_inline_alloc(tmp_ctx, fr_event_counter_t, node, timer_location_cmp, NULL);
1486 if (!locations[i]) goto oom;
1487 }
1488
1489 switch (tl->type) {
1491 /*
1492 * Show which events are due, when they're due,
1493 * and where they were allocated
1494 */
1495 for (ev = fr_lst_iter_init(tl->lst, &iter);
1496 ev != NULL;
1497 ev = fr_lst_iter_next(tl->lst, &iter)) {
1498 if (_event_report_process(locations, array, now, ev) < 0) goto oom;
1499 }
1500 break;
1501
1503 /*
1504 * Show which events are due, when they're due,
1505 * and where they were allocated
1506 */
1507 for (ev = timer_head(&tl->ordered);
1508 ev != NULL;
1509 ev = timer_next(&tl->ordered, ev)) {
1510 if (_event_report_process(locations, array, now, ev) < 0) goto oom;
1511 }
1512 break;
1513
1515 fr_assert(0);
1516 return;
1517 }
1518
1519 pthread_mutex_lock(&print_lock);
1520 EVENT_DEBUG("num timer events: %"PRIu64, fr_timer_list_num_events(tl));
1521
1522 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1523 fr_rb_iter_inorder_t event_iter;
1524 void *node;
1525
1526 if (!array[i]) continue;
1527
1528 if (i == 0) {
1529 EVENT_DEBUG(" events <= %5s : %zu", decade_names[i], array[i]);
1530 } else if (i == (NUM_ELEMENTS(decades) - 1)) {
1531 EVENT_DEBUG(" events > %5s : %zu", decade_names[i - 1], array[i]);
1532 } else {
1533 EVENT_DEBUG(" events %5s - %5s : %zu", decade_names[i - 1], decade_names[i], array[i]);
1534 }
1535
1536 for (node = fr_rb_iter_init_inorder(locations[i], &event_iter);
1537 node;
1538 node = fr_rb_iter_next_inorder(locations[i], &event_iter)) {
1539 fr_event_counter_t *counter = talloc_get_type_abort(node, fr_event_counter_t);
1540
1541 EVENT_DEBUG(" : %u allocd at %s[%d]",
1542 counter->count, counter->file, counter->line);
1543 }
1544 }
1545 pthread_mutex_unlock(&print_lock);
1546
1547 fr_timer_in(tl, tl, &tl->report, fr_time_delta_from_sec(EVENT_REPORT_FREQ), false, fr_timer_report, uctx);
1548 talloc_free(tmp_ctx);
1549}
1550
1551void fr_timer_dump(fr_timer_list_t *tl)
1552{
1553 fr_lst_iter_t iter;
1554 fr_timer_t *ev;
1555 fr_time_t now = tl->pub.time(); /* Get the current time */
1556
1557#define TIMER_DUMP(_ev) \
1558 EVENT_DEBUG("%s[%d]: %p time=%" PRId64 " (%c), callback=%p", \
1559 (_ev)->file, (_ev)->line, _ev, fr_time_unwrap((_ev)->when), \
1560 fr_time_gt(now, (_ev)->when) ? '<' : '>', (_ev)->callback);
1561
1562 EVENT_DEBUG("Time is now %"PRId64"", fr_time_unwrap(now));
1563
1564 switch (tl->type) {
1566 EVENT_DEBUG("Dumping lst timer list");
1567
1568 for (ev = fr_lst_iter_init(tl->lst, &iter);
1569 ev;
1570 ev = fr_lst_iter_next(tl->lst, &iter)) {
1571 (void)talloc_get_type_abort(ev, fr_timer_t);
1572 TIMER_DUMP(ev);
1573 }
1574 break;
1575
1577 EVENT_DEBUG("Dumping ordered timer list");
1578
1579 for (ev = timer_head(&tl->ordered);
1580 ev;
1581 ev = timer_next(&tl->ordered, ev)) {
1582 (void)talloc_get_type_abort(ev, fr_timer_t);
1583 TIMER_DUMP(ev);
1584 }
1585 break;
1586
1588 EVENT_DEBUG("Dumping shared timer list");
1589
1590 fr_rb_inorder_foreach(tl->shared.rb, void, uctx) {
1591 EVENT_DEBUG("time %" PRIu64" uctx %p", fr_time_unwrap(*TIMER_UCTX_TO_TIME(tl, uctx)), uctx);
1592 }}
1593 break;
1594 }
1595}
1596#endif
int const char * file
Definition acutest.h:855
void int line
Definition acutest.h:810
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:203
#define NDEBUG_LOCATION_FMT
Definition build.h:363
#define CC_NO_UBSAN(_sanitize)
Definition build.h:534
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:139
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:130
#define unlikely(_x)
Definition build.h:486
#define NDEBUG_LOCATION_VALS
Definition build.h:362
#define NDEBUG_LOCATION_ARGS
Pass caller information to the function.
Definition build.h:361
#define UNUSED
Definition build.h:415
#define NUM_ELEMENTS(_t)
Definition build.h:437
fr_cmp_ret_t
Result of an ordering comparison.
Definition build.h:113
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:177
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
Definition dlist.h:1150
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
Definition dlist.h:1173
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:257
#define FR_DLIST_TYPEDEFS(_name, _head, _entry)
Define friendly names for type specific dlist head and entry structures.
Definition dlist.h:1160
Head of a doubly linked list.
Definition dlist.h:51
Entry in a doubly linked list.
Definition dlist.h:41
#define EVENT_DEBUG(...)
Definition event.h:65
talloc_free(hp)
#define fr_time()
Definition event.c:58
void * fr_lst_iter_next(fr_lst_t *lst, fr_lst_iter_t *iter)
Get the next entry in an LST.
Definition lst.c:802
int fr_lst_extract(fr_lst_t *lst, void *data)
Remove an element from an LST.
Definition lst.c:734
void * fr_lst_iter_init(fr_lst_t *lst, fr_lst_iter_t *iter)
Iterate over entries in LST.
Definition lst.c:783
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
int fr_lst_insert(fr_lst_t *lst, void *data)
Definition lst.c:749
unsigned int fr_lst_num_elements(fr_lst_t *lst)
Definition lst.c:767
int fr_lst_peek(void **out, fr_lst_t *lst)
Definition lst.c:719
Definition lst.c:59
#define fr_lst_talloc_alloc(_ctx, _cmp, _talloc_type, _field, _init)
Creates an LST that verifies elements are of a specific talloc type.
Definition lst.h:75
fr_lst_index_t fr_lst_iter_t
Definition lst.h:46
unsigned int fr_lst_index_t
Definition lst.h:44
unsigned int uint32_t
unsigned char uint8_t
fr_cmp_ret_t(* fr_cmp_t)(void const *a, void const *b)
Definition misc.h:40
#define fr_assert(_expr)
Definition rad_assert.h:37
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition rb.c:807
int fr_rb_remove(void **removed, fr_rb_tree_t *tree, void const *data)
Remove an entry from the tree, without freeing the data.
Definition rb.c:718
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:586
void * fr_rb_iter_init_inorder(fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Initialise an in-order iterator.
Definition rb.c:850
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.
Definition rb.c:202
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.
Definition rb.c:748
void * fr_rb_first(fr_rb_tree_t *tree)
Definition rb.c:812
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:637
void * fr_rb_iter_next_inorder(UNUSED fr_rb_tree_t *tree, fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:876
#define fr_rb_inline_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black tree.
Definition rb.h:269
#define fr_rb_inorder_foreach(_tree, _type, _iter)
Definition rb.h:330
Iterator structure for in-order traversal of an rbtree.
Definition rb.h:319
The main red black tree structure.
Definition rb.h:71
return count
Definition module.c:155
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.
Definition talloc.c:168
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
#define fr_time_gteq(_a, _b)
Definition time.h:238
static int64_t fr_time_unwrap(fr_time_t time)
Definition time.h:146
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_eq(_a, _b)
Definition time.h:241
static fr_cmp_ret_t fr_time_cmp(fr_time_t a, fr_time_t b)
Compare two fr_time_t values.
Definition time.h:916
#define fr_time_add(_a, _b)
Add a time/time delta together.
Definition time.h:196
static fr_cmp_ret_t fr_time_delta_cmp(fr_time_delta_t a, fr_time_delta_t b)
Compare two fr_time_delta_t values.
Definition time.h:930
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
#define fr_time_lt(_a, _b)
Definition time.h:239
#define fr_time_max()
Definition time.h:143
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
TALLOC_CTX * linked_ctx
talloc ctx this event was bound to.
Definition timer.c:89
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
Execute any pending events in the event loop.
Definition timer.c:934
static uint64_t timer_list_ordered_num_events(fr_timer_list_t *tl)
Definition timer.c:1083
static fr_timer_list_t * timer_list_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Definition timer.c:1231
int fr_timer_list_disarm(fr_timer_list_t *tl)
Disarm a timer list.
Definition timer.c:1100
int(* timer_disarm_t)(fr_timer_t *ev)
Specialisation function to delete a timer.
Definition timer.c:157
timer_head_t deferred
A list of timer events to be inserted, after the current batch has been processed.
Definition timer.c:67
fr_time_t fr_timer_when(fr_timer_t *ev)
Internal timestamp representing when the timer should fire.
Definition timer.c:740
uint64_t(* timer_list_num_elements_t)(fr_timer_list_t *tl)
Return the number of elements in the list.
Definition timer.c:193
timer_list_num_elements_t num_events
Function to get the number of elements in the list.
Definition timer.c:202
timer_list_type_t type
Definition timer.c:63
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
Definition timer.c:1148
fr_timer_t ** parent
A pointer to the parent structure containing the timer event.
Definition timer.c:91
static int timer_list_lst_deferred(fr_timer_list_t *tl)
Move all deferred events into the lst.
Definition timer.c:1003
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.
Definition timer.c:286
int fr_timer_uctx_insert(fr_timer_list_t *tl, void *uctx)
Insert a uctx into a shared timer, and update the timer.
Definition timer.c:1347
static int _timer_free(fr_timer_t *ev)
Remove an event from the event loop.
Definition timer.c:408
static int timer_ordered_insert_at(fr_timer_list_t *tl, fr_timer_t *ev)
Insert an event into an ordered timer list.
Definition timer.c:383
fr_time_t fr_timer_list_when(fr_timer_list_t *tl)
Return the time of the next event.
Definition timer.c:1185
fr_timer_list_t * fr_timer_list_ordered_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new sorted event timer list.
Definition timer.c:1290
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.
Definition timer.c:363
bool _fr_timer_armed(fr_timer_t *ev)
Check if a timer event is armed.
Definition timer.c:763
fr_timer_t *(* timer_list_head_t)(fr_timer_list_t *tl)
Return the soonest timer event.
Definition timer.c:176
fr_time_delta_t fr_timer_remaining(fr_timer_t *ev)
Return time delta between now and when the timer should fire.
Definition timer.c:750
timer_disarm_t disarm
Function to delete a timer event.
Definition timer.c:197
#define TIMER_RETURN_DONE
Definition timer.c:133
static int _timer_list_free(fr_timer_list_t *tl)
Cleanup all timers currently in the list.
Definition timer.c:1211
void const * uctx
Context pointer to pass to the callback.
Definition timer.c:87
int(* timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when)
Specialisation function to execute any pending timers.
Definition timer.c:167
timer_list_head_t head
Function to get the head of the list.
Definition timer.c:200
int fr_timer_list_force_run(fr_timer_list_t *tl)
Forcibly run all events in an event loop.
Definition timer.c:916
fr_timer_cb_t callback
Callback to execute when the timer fires.
Definition timer.c:86
static int timer_lst_disarm(fr_timer_t *ev)
Definition timer.c:614
bool disarmed
the entire timer list is disarmed
Definition timer.c:65
int(* timer_list_deferred_t)(fr_timer_list_t *tl)
Process any deferred timer events.
Definition timer.c:185
fr_time_t when
When this timer should fire.
Definition timer.c:84
struct fr_timer_list_pub_s pub
Public interface to the event timer list.
Definition timer.c:50
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.
Definition timer.c:604
static int timer_list_parent_update(fr_timer_list_t *tl)
Utility function to update parent timers.
Definition timer.c:306
static fr_cmp_ret_t timer_cmp(void const *a, void const *b)
Compare two timer events to see which one should occur first.
Definition timer.c:272
static int timer_list_shared_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled events in an ordered list.
Definition timer.c:881
timer_insert_t insert
Function to insert a timer event.
Definition timer.c:196
fr_timer_t * parent_ev
Event in the parent's event loop.
Definition timer.c:73
bool free_on_fire
Whether to free the event when it fires.
Definition timer.c:99
fr_timer_entry_t entry
Entry in a list of timer events.
Definition timer.c:94
static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled timer events in a lst.
Definition timer.c:779
static int timer_list_shared_deferred(fr_timer_list_t *tl)
Move all deferred events into the shared list.
Definition timer.c:1064
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.
Definition timer.c:1311
fr_timer_list_t * fr_timer_list_lst_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Allocate a new lst based timer list.
Definition timer.c:1262
static timer_list_funcs_t const timer_funcs[]
Functions for performing operations on various types of timer list.
Definition timer.c:233
static int timer_ordered_disarm(fr_timer_t *ev)
Remove a timer from a timer list, but don't free it.
Definition timer.c:650
int fr_timer_disarm(fr_timer_t *ev)
Remove an event from the event list, but don't free the memory.
Definition timer.c:676
fr_timer_list_t * parent
Parent list to insert event into (if any).
Definition timer.c:72
#define CHECK_PARENT(_ev)
Definition timer.c:113
static fr_time_t * timer_list_when(fr_timer_list_t *tl)
Definition timer.c:1155
static int timer_list_ordered_run(fr_timer_list_t *tl, fr_time_t *when)
Run all scheduled events in an ordered list.
Definition timer.c:831
static fr_timer_t * timer_list_lst_head(fr_timer_list_t *tl)
Return the head of the lst.
Definition timer.c:974
static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl)
Definition timer.c:1078
int fr_timer_uctx_remove(fr_timer_list_t *tl, void *uctx)
Remove a uctx from a shared timer.
Definition timer.c:1370
int line
Line this event was last updated on.
Definition timer.c:107
void fr_timer_list_set_time_func(fr_timer_list_t *tl, fr_event_time_source_t func)
Override event list time source.
Definition timer.c:1199
#define TIMER_STOP_IF_DONE(_next)
Definition timer.c:125
timer_list_deferred_t deferred
Function to process deferred events.
Definition timer.c:201
void * fr_timer_uctx_peek(fr_timer_list_t *tl)
Definition timer.c:1380
static fr_timer_t * timer_list_ordered_head(fr_timer_list_t *tl)
Return the head of the ordered list.
Definition timer.c:990
#define TIMER_UCTX_TO_TIME(_tl, _x)
Definition timer.c:117
int fr_timer_list_arm(fr_timer_list_t *tl)
Arm (or re-arm) a timer list.
Definition timer.c:1121
bool in_handler
Whether we're currently in a callback.
Definition timer.c:64
#define EVENT_ARMED(_ev)
Definition timer.c:205
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
Definition timer.c:713
timer_list_type_t
What type of event list the timer is inserted into.
Definition timer.c:40
@ TIMER_LIST_TYPE_SHARED
all events share one event callback
Definition timer.c:43
@ TIMER_LIST_TYPE_LST
Self-sorting timer list based on a left leaning skeleton tree.
Definition timer.c:41
@ TIMER_LIST_TYPE_ORDERED
Strictly ordered list of events in a dlist.
Definition timer.c:42
char const * file
Source file this event was last updated in.
Definition timer.c:106
fr_timer_list_t * tl
The event list this timer is part of.
Definition timer.c:101
static int timer_list_ordered_deferred(fr_timer_list_t *tl)
Move all deferred events into the ordered event list.
Definition timer.c:1026
int(* timer_insert_t)(fr_timer_list_t *tl, fr_timer_t *ev)
Specialisation function to insert a timer.
Definition timer.c:148
timer_list_run_t run
Function to run a timer event.
Definition timer.c:199
static uint64_t timer_list_shared_num_events(fr_timer_list_t *tl)
Definition timer.c:1088
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.
Definition timer.c:442
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define FR_TIMER_DISARM_RETURN(_ev)
Definition timer.h:98
#define fr_timer_in(...)
Definition timer.h:87
fr_time_t(* fr_event_time_source_t)(void)
Alternative time source, useful for testing.
Definition timer.h:52
void(* fr_timer_cb_t)(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Called when a timer event fires.
Definition timer.h:75
static bool fr_timer_armed(fr_timer_t *ev)
Definition timer.h:124
#define fr_timer_at(...)
Definition timer.h:81
fr_event_time_source_t _CONST time
Time source this list uses to get the current time when calculating deltas (fr_timer_in).
Definition timer.h:61
Public event timer list structure.
Definition timer.h:60
static fr_time_delta_t time_offset
static fr_slen_t head
Definition xlat.h:410
static fr_slen_t parent
Definition pair.h:864
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
char const * fr_strerror_peek(void)
Get the last library error.
Definition strerror.c:631
#define fr_strerror_const_push(_msg)
Definition strerror.h:227
#define fr_strerror_const(_msg)
Definition strerror.h:223