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