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