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.
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 };
57 bool in_handler; //!< Whether we're currently in a callback.
58
59 timer_head_t deferred; //!< A list of timer events to be inserted, after
60 ///< the current batch has been processed.
61 ///< This prevents "busy" timer loops, where
62 ///< other events may starve, or we may never exit.
63
64 fr_timer_list_t *parent; //!< Parent list to insert event into (if any).
65 fr_timer_t *parent_ev; //!< Event in the parent's event loop.
66
67#ifdef WITH_EVENT_DEBUG
68 fr_timer_t *report; //!< Used to trigger periodic reports about the event timer list.
69#endif
70};
71
72/** A timer event
73 *
74 */
75struct fr_timer_s {
76 fr_time_t when; //!< When this timer should fire.
77
78 fr_timer_cb_t callback; //!< Callback to execute when the timer fires.
79 void const *uctx; //!< Context pointer to pass to the callback.
80
81 TALLOC_CTX *linked_ctx; //!< talloc ctx this event was bound to.
82
83 fr_timer_t **parent; //!< A pointer to the parent structure containing the timer
84 ///< event.
85
86 fr_timer_entry_t entry; //!< Entry in a list of timer events.
87 union {
88 fr_dlist_t ordered_entry; //!< Entry in an ordered list of timer events.
89 fr_lst_index_t lst_idx; //!< Where to store opaque lst data, not used for ordered lists.
90 };
91 bool free_on_fire; //!< Whether to free the event when it fires.
92
93 fr_timer_list_t *tl; //!< The event list this timer is part of.
94 ///< This is set to NULL when an event is disarmed,
95 ///< but all other fields are left intact.
96
97#ifndef NDEBUG
98 char const *file; //!< Source file this event was last updated in.
99 int line; //!< Line this event was last updated on.
100#endif
101};
102
103FR_DLIST_FUNCS(timer, fr_timer_t, entry)
104
105#define CHECK_PARENT(_ev) \
106 fr_assert_msg(!(_ev)->parent || (*(_ev)->parent == ev), \
107 "Event %p, allocd %s[%d], parent field points to %p", (_ev), (_ev)->file, (_ev)->line, *(_ev)->parent);
108
109/** Specialisation function to insert a timer
110 *
111 * @param[in] tl Timer list to insert into.
112 * @param[in] ev Timer event to insert.
113 * @return
114 * - 0 on success.
115 * - -1 on failure.
116 */
118
119/** Specialisation function to delete a timer
120 *
121 * @param[in] ev Timer event to delete.
122 * @return
123 * - 0 on success.
124 * - -1 on failure.
125 */
126typedef int (*timer_disarm_t)(fr_timer_t *ev);
127
128/** Specialisation function to execute any pending timers
129 *
130 * @param[in] tl Timer list to execute.
131 * @param[in,out] when Our current time, updated to the next event time (i.e. the next time we'll need to run something)
132 * @return
133 * - 0 no timer events fired.
134 * - 1 a timer event fired.
135 */
136typedef int (*timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when);
137
138/** Return the soonest timer event
139 *
140 * @param[in] tl to get the head of.
141 * @return
142 * - The head of the list.
143 * - NULL if the list is empty.
144 */
145typedef fr_timer_t *(*timer_list_head_t)(fr_timer_list_t *tl);
146
147/** Process any deferred timer events
148 *
149 * @param[in] tl to process deferred events for.
150 * @return
151 * - The head of the list.
152 * - NULL if the list is empty.
153 */
155
156/** Return the number of elements in the list
157 *
158 * @param[in] tl to get the number of elements from.
159 * @return
160 * - The number of elements in the list.
161 */
163
164typedef struct {
165 timer_insert_t insert; //!< Function to insert a timer event.
166 timer_disarm_t disarm; //!< Function to delete a timer event.
167
168 timer_list_run_t run; //!< Function to run a timer event.
169 timer_list_head_t head; //!< Function to get the head of the list.
170 timer_list_deferred_t deferred; //!< Function to process deferred events.
171 timer_list_num_elements_t num_events; //!< Function to get the number of elements in the list.
173
174#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
175
178
179static int timer_lst_disarm(fr_timer_t *ev);
180static int timer_ordered_disarm(fr_timer_t *ev);
181
182static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when);
184
187
190
191static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl);
193
194/** Functions for performing operations on various types of timer list
195 *
196 */
200 .disarm = timer_lst_disarm,
201
202 .run = timer_list_lst_run,
203 .head = timer_list_lst_head,
204 .deferred = timer_list_lst_deferred,
205 .num_events = timer_list_lst_num_events
206 },
208 .insert = timer_ordered_insert_at,
209 .disarm = timer_ordered_disarm,
210
213 .deferred = timer_list_ordered_deferred,
215 }
216};
217
218/** Compare two timer events to see which one should occur first
219 *
220 * @param[in] a the first timer event.
221 * @param[in] b the second timer event.
222 * @return
223 * - +1 if a should occur later than b.
224 * - -1 if a should occur earlier than b.
225 * - 0 if both events occur at the same time.
226 */
227static int8_t timer_cmp(void const *a, void const *b)
228{
229 fr_timer_t const *ev_a = a, *ev_b = b;
230
231 return fr_time_cmp(ev_a->when, ev_b->when);
232}
233
234/** This callback fires in the parent to execute events in this sublist
235 *
236 * @param[in] parent_tl Parent event timer list.
237 * @param[in] when When the parent timer fired.
238 * @param[in] uctx Sublist to execute.
239 */
240static void _parent_timer_cb(UNUSED fr_timer_list_t *parent_tl, fr_time_t when, void *uctx)
241{
242 /*
243 * We're in the parent timer, so we need to run the
244 * events in the child timer list.
245 */
246 (void)fr_timer_list_run(talloc_get_type_abort(uctx, fr_timer_list_t), &when);
247}
248
249/** Utility function to update parent timers
250 *
251 * @param[in] tl to update parent timers for.
252 * @return
253 * - 0 on success.
254 * - -1 on failure.
255 */
256static inline CC_HINT(always_inline) int timer_list_parent_update(fr_timer_list_t *tl)
257{
258 fr_timer_t *ev;
259
260 if (!tl->parent) return 0;
261
262 ev = timer_funcs[tl->type].head(tl);
263 /*
264 * No events, disarm the timer
265 */
266 if (!ev) {
267 /*
268 * Disables the timer in the parent, does not free the memory
269 */
271 return 0;
272 }
273
274 if (tl->parent_ev && EVENT_ARMED(tl->parent_ev) &&
275 fr_time_eq(ev->when, tl->parent_ev->when)) return 0; /* noop */
276
277 /*
278 * Re-arm the timer
279 */
280 return fr_timer_at(tl, tl->parent, &tl->parent_ev,
281 ev->when, false, _parent_timer_cb, tl);
282}
283
284/** Insert a timer event into a single event timer list
285 *
286 * @param[in] tl to insert the event into.
287 * @param[in] ev to insert.
288 * @return
289 * - 0 on success.
290 * - -1 on failure.
291 */
293{
294 if (unlikely(fr_lst_insert(tl->lst, ev) < 0)) {
295 fr_strerror_const_push("Failed inserting timer into lst");
296 return -1;
297 }
298
299 return 0;
300}
301
302/** Insert an event into an ordered timer list
303 *
304 * Timer must be in order, i.e. either before first event, or after last event
305 *
306 * @param[in] tl to insert the event into.
307 * @param[in] ev to insert.
308 * @return
309 * - 0 on success.
310 * - -1 on failure.
311 */
313{
314 fr_timer_t *tail;
315
316 tail = timer_tail(&tl->ordered);
317 if (tail && fr_time_lt(ev->when, tail->when)) {
318 fr_strerror_const("Event being inserted must occurr _after_ the last event");
319 return -1;
320 }
321
322 if (unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
323 fr_strerror_const_push("Failed inserting timer into ordered list");
324 return -1;
325 }
326
327 return 0;
328}
329
330/** Remove an event from the event loop
331 *
332 * @param[in] ev to free.
333 * @return
334 * - 0 on success.
335 * - -1 on failure.
336 */
337static int _timer_free(fr_timer_t *ev)
338{
339 fr_timer_t **ev_p;
340 int ret;
341
342 ret = fr_timer_disarm(ev); /* Is a noop if ev->tl == NULL */
343 if (ret < 0) return ret;
344
345 CHECK_PARENT(ev);
346 ev_p = ev->parent;
347 *ev_p = NULL;
348
349 return 0;
350}
351
352/** Insert a timer event into an event list
353 *
354 * @note The talloc parent of the memory returned in ev_p must not be changed.
355 * If the lifetime of the event needs to be bound to another context
356 * this function should be called with the existing event pointed to by
357 * ev_p.
358 *
359 * @param[in] ctx to bind lifetime of the event to.
360 * @param[in] tl to insert event into.
361 * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
362 * in a temporal sense, not in a memory structure or dependency sense.
363 * @param[in] when we should run the event.
364 * @param[in] free_on_fire Whether event memory should be freed if the event fires.
365 * @param[in] callback function to execute if the event fires.
366 * @param[in] uctx user data to pass to the event.
367 * @return
368 * - 0 on success.
369 * - -1 on failure.
370 */
372 TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
373 fr_time_t when,
374 bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
375{
376 fr_timer_t *ev;
377
378 /*
379 * If there is an event, reuse it instead of freeing it
380 * and allocating a new one. This is to reduce memory
381 * churn for repeat events.
382 */
383 if (!*ev_p) {
384 new_event:
385 ev = talloc_zero(tl, fr_timer_t);
386 if (unlikely(!ev)) {
387 fr_strerror_const("Out of memory");
388 return -1;
389 }
390
391 EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Added new timer %p", tl, NDEBUG_LOCATION_VALS ev);
392 /*
393 * Bind the lifetime of the event to the specified
394 * talloc ctx. If the talloc ctx is freed, the
395 * event will also be freed.
396 */
397 if (ctx != tl) talloc_link_ctx(ctx, ev);
398
399 talloc_set_destructor(ev, _timer_free);
400 } else {
401 ev = talloc_get_type_abort(UNCONST(fr_timer_t *, *ev_p), fr_timer_t);
402
403 EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Re-armed timer %p", tl, NDEBUG_LOCATION_VALS ev);
404
405 /*
406 * We can't disarm the linking context due to
407 * limitations in talloc, so if the linking
408 * context changes, we need to free the old
409 * event, and allocate a new one.
410 *
411 * Freeing the event also removes it from the lst.
412 */
413 if (unlikely(ev->linked_ctx != ctx)) {
414 talloc_free(ev);
415 goto new_event;
416 }
417
418 /*
419 * If the event is associated with a list, we need
420 * to disarm it, before we can rearm it.
421 */
422 if (EVENT_ARMED(ev)) {
423 int ret;
424 char const *err_file;
425 int err_line;
426
427 /*
428 * Removed event from the event list or the
429 * deferred list.
430 */
431 ret = fr_timer_disarm(ev);
432#ifndef NDEBUG
433 err_file = ev->file;
434 err_line = ev->line;
435#else
436 err_file = "not-available";
437 err_line = 0;
438#endif
439
440 /*
441 * Events MUST be in the lst (or the insertion list).
442 */
443 if (!fr_cond_assert_msg(ret == 0,
444 "Event %p, allocd %s[%d], was not found in the event "
445 "list or deferred list when re-armed: %s", ev,
446 err_file, err_line, fr_strerror())) return -1;
447 }
448 }
449
450 ev->tl = tl; /* This indicates the event memory is bound to an avent loop */
451 ev->when = when;
452 ev->free_on_fire = free_on_fire;
453 ev->callback = callback;
454 ev->uctx = uctx;
455 ev->linked_ctx = ctx;
456 ev->parent = ev_p;
457#ifndef NDEBUG
458 ev->file = file;
459 ev->line = line;
460#endif
461
462 /*
463 * No updating needed as the events are deferred
464 */
465 if (tl->in_handler) {
466 /*
467 * ...a little hacky, but we need to verify that
468 * we're not inserting an event that's earlier
469 * than the last event in the list for ordered
470 * lists.
471 *
472 * Otherwise we'd end up doing this when we tried
473 * to move all the deferred events into the timer
474 * list, and end up making that O(n) instead of O(1).
475 */
476 if (tl->type == TIMER_LIST_TYPE_ORDERED) {
478
479 if (head && fr_time_lt(ev->when, head->when)) {
480 fr_strerror_const("Event being inserted must occurr _after_ the last event");
481
482 insert_failed:
483 talloc_set_destructor(ev, NULL);
484 talloc_free(ev);
485 *ev_p = NULL;
486 return -1;
487 }
488 }
489
490 if (!fr_cond_assert_msg(timer_insert_tail(&tl->deferred, ev) == 0,
491 "Failed inserting event into deferred list")) {
492 goto insert_failed;
493 }
494 } else {
495 int ret;
496
497 ret = timer_funcs[tl->type].insert(tl, ev);
498 if (unlikely(ret < 0)) goto insert_failed;
499
500 /*
501 * We need to update the parent timer
502 * to ensure it fires at the correct time.
503 */
504 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
505 }
506
507 *ev_p = ev;
508
509 return 0;
510}
511
512/** Insert a timer event into an event list
513 *
514 * @note The talloc parent of the memory returned in ev_p must not be changed.
515 * If the lifetime of the event needs to be bound to another context
516 * this function should be called with the existing event pointed to by
517 * ev_p.
518 *
519 * @param[in] ctx to bind lifetime of the event to.
520 * @param[in] tl to insert event into.
521 * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
522 * in a temporal sense, not in a memory structure or dependency sense.
523 * @param[in] delta In how many nanoseconds to wait before should we execute the event.
524 * @param[in] callback function to execute if the event fires.
525 * @param[in] uctx user data to pass to the event.
526 * @return
527 * - 0 on success.
528 * - -1 on failure.
529 */
531 TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
532 fr_time_delta_t delta,
533 bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
534{
536 ctx, tl, ev_p, fr_time_add(tl->pub.time(), delta),
537 free_on_fire, callback, uctx);
538}
539
541{
542 fr_timer_list_t *tl = ev->tl;
543
544 if (timer_in_list(&tl->deferred,ev)) {
545 (void)timer_remove(&tl->deferred, ev);
546 } else {
547 int ret = fr_lst_extract(tl->lst, ev);
548 char const *err_file;
549 int err_line;
550
551#ifndef NDEBUG
552 err_file = ev->file;
553 err_line = ev->line;
554#else
555 err_file = "not-available";
556 err_line = 0;
557#endif
558
559
560 /*
561 * Events MUST be in the lst (or the insertion list).
562 */
563 if (!fr_cond_assert_msg(ret == 0,
564 "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
565 "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
566 fr_strerror())) return -1;
567 }
568
569 return 0;
570}
571
572/** Remove a timer from a timer list, but don't free it
573 *
574 * @param[in] ev to remove.
575 */
577{
578 /*
579 * Check the check is still valid (sanity check)
580 */
581 (void)talloc_get_type_abort(ev, fr_timer_t);;
582
583 /*
584 * Already dissassociated from a list, nothing to do.
585 */
586 if (!ev->tl) return 0;
587
588 /*
589 * This *MUST* be in a timer list if it has a non-NULL tl pointer.
590 */
591 if (unlikely(!fr_cond_assert(timer_in_list(&ev->tl->ordered, ev)))) return -1;
592
593 (void)timer_remove(&ev->tl->ordered, ev);
594
595 return 0;
596}
597
598/** Remove an event from the event list, but don't free the memory
599 *
600 * @param[in] ev to remove from the event list.
601 */
603{
604 fr_timer_list_t *tl;
605
606 if (!ev || !EVENT_ARMED(ev)) {
607 EVENT_DEBUG("Asked to disarm inactive timer %p (noop)", ev);
608 return 0; /* Noop */
609 }
610
611 tl = ev->tl;
612
613 EVENT_DEBUG("Disarming timer %p", ev);
614
615 CHECK_PARENT(ev);
616
617 /*
618 * If the event is deferred, it's not in the event list proper
619 * so just remove it, and set the tl pointer to NULL.
620 */
621 if (timer_in_list(&tl->deferred,ev)) {
622 (void)timer_remove(&tl->deferred, ev);
623 } else {
624 int ret = timer_funcs[ev->tl->type].disarm(ev);
625 if (ret < 0) return ret;
626 }
627 ev->tl = NULL;
628
629 return timer_list_parent_update(tl);
630}
631
632/** Delete a timer event and free its memory
633 *
634 * @param[in] ev_p of the event being deleted.
635 * @return
636 * - 0 on success.
637 * - -1 on failure.
638 */
640{
641 fr_timer_t *ev;
642 int ret;
643
644 if (unlikely(!*ev_p)) return 0;
645
646 ev = *ev_p;
647 ret = talloc_free(ev); /* Destructor removed event from any lists */
648
649 /*
650 * Don't leave a garbage pointer value
651 * if parent is not ev_p.
652 */
653 if (likely(ret == 0)) {
654 *ev_p = NULL;
655 } else {
656 EVENT_DEBUG("Deleting timer %p failed: %s", ev, fr_strerror_peek());
657 }
658
659 return 0;
660}
661
662/** Internal timestamp representing when the timer should fire
663 *
664 * @return When the timestamp should fire.
665 */
667{
668 return ev->when;
669}
670
671/** Check if a timer event is armed
672 *
673 * @param[in] ev to check.
674 * @return
675 * - true if the event is armed.
676 * - false if the event is not armed.
677 */
679{
680 return EVENT_ARMED(ev);
681}
682
683/** Run all scheduled timer events in a lst
684 *
685 * @param[in] tl containing the timer events.
686 * @param[in] when Process events scheduled to run before or at this time.
687 * - Set to 0 if no more events.
688 * - Set to the next event time if there are more events.
689 * @return
690 * - 0 no timer events fired.
691 * - 1 a timer event fired.
692 */
693CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
695{
696 fr_timer_cb_t callback;
697 void *uctx;
698 fr_timer_t *ev;
699 int fired = 0;
700
701 while (fr_lst_num_elements(tl->lst) > 0) {
702 ev = talloc_get_type_abort(fr_lst_peek(tl->lst), fr_timer_t);
703
704 /*
705 * See if it's time to do this one.
706 */
707 if (fr_time_gt(ev->when, *when)) {
708 *when = ev->when;
709 done:
710 return fired;
711 }
712
713 callback = ev->callback;
714 memcpy(&uctx, &ev->uctx, sizeof(uctx));
715
716 CHECK_PARENT(ev);
717
718 /*
719 * Disarm the event before calling it.
720 *
721 * This leaves the memory in place,
722 * but dissassociates it from the list.
723 *
724 * We use the public function as it
725 * handles more cases.
726 */
727 if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
728 EVENT_DEBUG("Running timer %p", ev);
729 if (ev->free_on_fire) talloc_free(ev);
730
731 callback(tl, *when, uctx);
732
733 fired++;
734 }
735
736 *when = fr_time_wrap(0);
737
738 goto done;
739}
740
741/** Run all scheduled events in an ordered list
742 *
743 * @param[in] tl containing the timer events.
744 * @param[in] when Process events scheduled to run before or at this time.
745 * - Set to 0 if no more events.
746 * - Set to the next event time if there are more events.
747 * @return
748 * - < 0 if we failed to updated the parent list.
749 * - 0 no timer events fired.
750 * - >0 number of timer event fired.
751 */
752CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
754{
755 fr_timer_cb_t callback;
756 void *uctx;
757 fr_timer_t *ev;
758 unsigned int fired = 0;
759
760 while ((ev = timer_head(&tl->ordered))) {
761 (void)talloc_get_type_abort(ev, fr_timer_t);
762
763 /*
764 * See if it's time to do this one.
765 */
766 if (fr_time_gt(ev->when, *when)) {
767 *when = ev->when;
768 done:
769 return fired;
770 }
771
772 callback = ev->callback;
773 memcpy(&uctx, &ev->uctx, sizeof(uctx));
774
775 CHECK_PARENT(ev);
776
777 /*
778 * Disarm the event before calling it.
779 *
780 * This leaves the memory in place,
781 * but dissassociates it from the list.
782 *
783 * We use the public function as it
784 * handles more cases.
785 */
786 if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
787 EVENT_DEBUG("Running timer %p", ev);
788 if (ev->free_on_fire) talloc_free(ev);
789
790 callback(tl, *when, uctx);
791
792 fired++;
793 }
794
795 *when = fr_time_wrap(0);
796
797 goto done;
798}
799
800/** Get the head of the timer list, the event may not be ready to fire
801 *
802 * This is used to forcefully run every event in the event loop.
803 *
804 * We pass in the real time, which may theoretically cause issues if timer
805 * callbacks are checking... But the uses of this function are very limited.
806 *
807 * @return
808 * - < 0 if we failed to update the parent list.
809 * - 0 no timer events fired.
810 * - > 0 number of timer event fired.
811 */
813{
814 fr_time_t when = fr_time_max();
815
816 return fr_timer_list_run(tl, &when);
817}
818
819/** Execute any pending events in the event loop
820 *
821 * @param[in] tl to execute events in.
822 * @param[in] when Process events scheduled to run before or at this time.
823 * - Set to 0 if no more events.
824 * - Set to the next event time if there are more events.
825 * @return
826 * - < 0 if we failed to update the parent list.
827 * - 0 no timer events fired.
828 * - >0 number of timer event fired.
829 */
831{
832 int ret;
833 bool in_handler = tl->in_handler; /* allow nested timer execution */
834
835 tl->in_handler = true;
836 ret = timer_funcs[tl->type].run(tl, when);
837 tl->in_handler = in_handler;
838
839 /*
840 * Now we've executed all the pending events,
841 * now merge the deferred events into the main
842 * event list.
843 *
844 * The events don't need to be modified as they
845 * were initialised completely before being
846 * placed in the deffered list.
847 */
848 if (timer_num_elements(&tl->deferred) > 0) {
849 if (unlikely(timer_funcs[tl->type].deferred(tl) < 0)) return -1;
850 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
851 /*
852 * We ran some events, and have no deferred
853 * events to insert, so we need to forcefully
854 * update the parent timer.
855 */
856 } else if(ret > 0) {
857 if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
858 }
859
860 return ret;
861}
862
863/** Return the head of the event list
864 *
865 * @param[in] tl to get the head of.
866 * @return
867 * - The head of the trie.
868 * - NULL, if there's no head.
869 */
871{
872 return fr_lst_peek(tl->lst);
873}
874
875/** Return the head of the ordered list
876 *
877 * @param[in] tl to get the head of.
878 * @return
879 * - The head of the trie.
880 * - NULL, if there's no head.
881 */
883{
884 return timer_head(&tl->ordered);
885}
886
887/** Insert a timer event into a the lst
888 *
889 * @param[in] tl to move events in.
890 * @return
891 * - 0 on success.
892 * - -1 on failure.
893 */
895{
896 fr_timer_t *ev;
897
898 while((ev = timer_pop_head(&tl->deferred))) {
899 if (unlikely(timer_lst_insert_at(tl, ev)) < 0) {
900 timer_insert_head(&tl->deferred, ev); /* Don't lose track of events we failed to insert */
901 return -1;
902 }
903 }
904
905 return 0;
906}
907
908/** Move all deferred events into the ordered event list
909 *
910 * This operation is O(1).
911 *
912 * @param[in] tl to move events in.
913 * @return
914 * - 0 on success.
915 * - -1 on failure.
916 */
918{
919 fr_timer_t *ev;
920#ifndef NDEBUG
921 {
922 fr_timer_t *head, *tail;
923
924 head = timer_head(&tl->deferred);
925 tail = timer_tail(&tl->ordered);
926
927 /*
928 * Something has gone catastrophically wrong if the
929 * deferred event is earlier than the last event in
930 * the ordered list, given all the checks we do.
931 */
932 fr_cond_assert_msg(!head || !tail || fr_time_gteq(head->when, tail->when),
933 "Deferred event is earlier than the last event in the ordered list");
934 }
935#endif
936
937 /*
938 * Can't use timer_move_head as entry positions
939 * for the two lists are different.
940 */
941 while ((ev = timer_pop_head((&tl->deferred)))) {
942 timer_insert_tail(&tl->ordered, ev);
943 }
944
945 return 0;
946}
947
949{
950 return fr_lst_num_elements(tl->lst);
951}
952
954{
955 return timer_num_elements(&tl->ordered);
956}
957
958/** Disable all timers in a list
959 *
960 */
962{
963 fr_timer_t *ev;
964
965 while ((ev = timer_funcs[tl->type].head(tl))) {
966 if (unlikely(fr_timer_disarm(ev) < 0)) return -1;
967 }
968
969 return 0;
970}
971
972/** Return number of pending events
973 *
974 * @note This includes deferred events, i.e. those yet to be inserted into the main list
975 *
976 * @param[in] tl to get the number of events from.
977 * @return
978 * - The number of events in the list.
979 */
981{
982 uint64_t num = timer_funcs[tl->type].num_events(tl);
983
984 return num + timer_num_elements(&tl->deferred);
985}
986
987/** Return the time of the next event
988 *
989 * @param[in] tl to get the next event time from.
990 * @return
991 * - >0 the time of the next event.
992 * - 0 if there are no more events.
993 */
995{
996 fr_timer_t *ev = timer_funcs[tl->type].head(tl);
997
998 if (ev) return ev->when;
999
1000 return fr_time_wrap(0);
1001}
1002
1003/** Override event list time source
1004 *
1005 * @param[in] tl to set new time function for.
1006 * @param[in] func to set.
1007 */
1012
1013/** Cleanup all timers currently in the list
1014 *
1015 * @param[in] tl to cleanup.
1016 * @return
1017 * - 0 on success.
1018 * - -1 on failure.
1019 */
1021{
1022 fr_timer_t *ev;
1023
1024 if (unlikely(tl->in_handler)) {
1025 fr_strerror_const("Cannot free event timer list while in handler");
1026 return -1;
1027 }
1028
1029 if (tl->parent_ev) if (unlikely(fr_timer_delete(&tl->parent_ev) < 0)) return -1;
1030
1031 while ((ev = timer_funcs[tl->type].head(tl))) {
1032 if (talloc_free(ev) < 0) return -1;
1033 }
1034
1035 return 0;
1036}
1037
1039{
1040 fr_timer_list_t *tl;
1041
1042 tl = talloc_zero(ctx, fr_timer_list_t);
1043 if (unlikely(tl == NULL)) {
1044 fr_strerror_const("Out of memory");
1045 return NULL;
1046 }
1047
1048 timer_talloc_init(&tl->deferred);
1049 if (parent) {
1050 tl->parent = parent;
1051 tl->pub.time = parent->pub.time;
1052 } else {
1053 tl->pub.time = fr_time;
1054 }
1055 talloc_set_destructor(tl, _timer_list_free);
1056
1057 return tl;
1058}
1059
1060/** Allocate a new lst based timer list
1061 *
1062 * @param[in] ctx to insert head timer event into.
1063 * @param[in] parent to insert the head timer event into.
1064 */
1066{
1067 fr_timer_list_t *tl;
1068
1069 if (unlikely((tl = timer_list_alloc(ctx, parent)) == NULL)) return NULL;
1070
1071 tl->lst = fr_lst_talloc_alloc(tl, timer_cmp, fr_timer_t, lst_idx, 0);
1072 if (unlikely(tl->lst == NULL)) {
1073 fr_strerror_const("Failed allocating timer list");
1074 talloc_free(tl);
1075 return NULL;
1076 }
1078
1079#ifdef WITH_EVENT_REPORT
1080 fr_timer_in(tl, tl, &tl->report, fr_time_delta_from_sec(EVENT_REPORT_FREQ), false, fr_timer_report, NULL);
1081#endif
1082
1083 return tl;
1084}
1085
1086/** Allocate a new sorted event timer list
1087 *
1088 * @param[in] ctx to allocate the event timer list from.
1089 * @param[in] parent to insert the head timer event into.
1090 */
1092{
1093 fr_timer_list_t *tl;
1094
1095 if (unlikely((tl = timer_list_alloc(ctx, parent)) == NULL)) return NULL;
1096
1097 fr_dlist_talloc_init((fr_dlist_head_t *)&tl->ordered, fr_timer_t, ordered_entry);
1099
1100 return tl;
1101}
1102
1103#if defined(WITH_EVENT_DEBUG) && !defined(NDEBUG)
1104static const fr_time_delta_t decades[18] = {
1105 { 1 }, { 10 }, { 100 },
1106 { 1000 }, { 10000 }, { 100000 },
1107 { 1000000 }, { 10000000 }, { 100000000 },
1108 { 1000000000 }, { 10000000000 }, { 100000000000 },
1109 { 1000000000000 }, { 10000000000000 }, { 100000000000000 },
1110 { 1000000000000000 }, { 10000000000000000 }, { 100000000000000000 },
1111};
1112
1113static const char *decade_names[18] = {
1114 "1ns", "10ns", "100ns",
1115 "1us", "10us", "100us",
1116 "1ms", "10ms", "100ms",
1117 "1s", "10s", "100s",
1118 "1Ks", "10Ks", "100Ks",
1119 "1Ms", "10Ms", "100Ms", /* 1 year is 300Ms */
1120};
1121
1122typedef struct {
1123 fr_rb_node_t node;
1124 char const *file;
1125 int line;
1127} fr_event_counter_t;
1128
1129static int8_t timer_location_cmp(void const *one, void const *two)
1130{
1131 fr_event_counter_t const *a = one;
1132 fr_event_counter_t const *b = two;
1133
1134 CMP_RETURN(a, b, file);
1135
1136 return CMP(a->line, b->line);
1137}
1138
1139static int _event_report_process(fr_rb_tree_t **locations, size_t array[], fr_time_t now, fr_timer_t *ev)
1140{
1141 fr_time_delta_t diff = fr_time_sub(ev->when, now);
1142 size_t i;
1143
1144 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1145 if ((fr_time_delta_cmp(diff, decades[i]) <= 0) || (i == NUM_ELEMENTS(decades) - 1)) {
1146 fr_event_counter_t find = { .file = ev->file, .line = ev->line };
1147 fr_event_counter_t *counter;
1148
1149 counter = fr_rb_find(locations[i], &find);
1150 if (!counter) {
1151 counter = talloc(locations[i], fr_event_counter_t);
1152 if (!counter) {
1153 EVENT_DEBUG("Can't do report, out of memory");
1154 return -1;
1155 }
1156 counter->file = ev->file;
1157 counter->line = ev->line;
1158 counter->count = 1;
1159 fr_rb_insert(locations[i], counter);
1160 } else {
1161 counter->count++;
1162 }
1163
1164 array[i]++;
1165 break;
1166 }
1167 }
1168
1169 return 0;
1170}
1171
1172/** Print out information about timer events in the event loop
1173 *
1174 */
1175void fr_timer_report(fr_timer_list_t *tl, fr_time_t now, void *uctx)
1176{
1177 fr_lst_iter_t iter;
1178 fr_timer_t *ev;
1179 size_t i;
1180
1181 size_t array[NUM_ELEMENTS(decades)] = { 0 };
1182 fr_rb_tree_t *locations[NUM_ELEMENTS(decades)];
1183 TALLOC_CTX *tmp_ctx;
1184 static pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER;
1185
1186 tmp_ctx = talloc_init_const("temporary stats");
1187 if (!tmp_ctx) {
1188 oom:
1189 EVENT_DEBUG("Can't do report, out of memory");
1190 talloc_free(tmp_ctx);
1191 return;
1192 }
1193
1194 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1195 locations[i] = fr_rb_inline_alloc(tmp_ctx, fr_event_counter_t, node, timer_location_cmp, NULL);
1196 if (!locations[i]) goto oom;
1197 }
1198
1199 switch (tl->type) {
1201 /*
1202 * Show which events are due, when they're due,
1203 * and where they were allocated
1204 */
1205 for (ev = fr_lst_iter_init(tl->lst, &iter);
1206 ev != NULL;
1207 ev = fr_lst_iter_next(tl->lst, &iter)) {
1208 if (_event_report_process(locations, array, now, ev) < 0) goto oom;
1209 }
1210 break;
1211
1213 /*
1214 * Show which events are due, when they're due,
1215 * and where they were allocated
1216 */
1217 for (ev = timer_head(&tl->ordered);
1218 ev != NULL;
1219 ev = timer_next(&tl->ordered, ev)) {
1220 if (_event_report_process(locations, array, now, ev) < 0) goto oom;
1221 }
1222 break;
1223 }
1224
1225 pthread_mutex_lock(&print_lock);
1226 EVENT_DEBUG("num timer events: %"PRIu64, fr_timer_list_num_events(tl));
1227
1228 for (i = 0; i < NUM_ELEMENTS(decades); i++) {
1229 fr_rb_iter_inorder_t event_iter;
1230 void *node;
1231
1232 if (!array[i]) continue;
1233
1234 if (i == 0) {
1235 EVENT_DEBUG(" events <= %5s : %zu", decade_names[i], array[i]);
1236 } else if (i == (NUM_ELEMENTS(decades) - 1)) {
1237 EVENT_DEBUG(" events > %5s : %zu", decade_names[i - 1], array[i]);
1238 } else {
1239 EVENT_DEBUG(" events %5s - %5s : %zu", decade_names[i - 1], decade_names[i], array[i]);
1240 }
1241
1242 for (node = fr_rb_iter_init_inorder(&event_iter, locations[i]);
1243 node;
1244 node = fr_rb_iter_next_inorder(&event_iter)) {
1245 fr_event_counter_t *counter = talloc_get_type_abort(node, fr_event_counter_t);
1246
1247 EVENT_DEBUG(" : %u allocd at %s[%d]",
1248 counter->count, counter->file, counter->line);
1249 }
1250 }
1251 pthread_mutex_unlock(&print_lock);
1252
1253 fr_timer_in(tl, tl, &tl->report, fr_time_delta_from_sec(EVENT_REPORT_FREQ), false, fr_timer_report, uctx);
1254 talloc_free(tmp_ctx);
1255}
1256
1257void fr_timer_dump(fr_timer_list_t *tl)
1258{
1259 fr_lst_iter_t iter;
1260 fr_timer_t *ev;
1261 fr_time_t now = tl->pub.time(); /* Get the current time */
1262
1263#define TIMER_DUMP(_ev) \
1264 EVENT_DEBUG("%s[%d]: %p time=%" PRId64 " (%c), callback=%p", \
1265 (_ev)->file, (_ev)->line, _ev, fr_time_unwrap((_ev)->when), \
1266 fr_time_gt(now, (_ev)->when) ? '<' : '>', (_ev)->callback);
1267
1268 EVENT_DEBUG("Time is now %"PRId64"", fr_time_unwrap(now));
1269
1270 switch (tl->type) {
1272 EVENT_DEBUG("Dumping lst timer list");
1273
1274 for (ev = fr_lst_iter_init(tl->lst, &iter);
1275 ev;
1276 ev = fr_lst_iter_next(tl->lst, &iter)) {
1277 (void)talloc_get_type_abort(ev, fr_timer_t);
1278 TIMER_DUMP(ev);
1279 }
1280 break;
1281
1283 EVENT_DEBUG("Dumping ordered timer list");
1284
1285 for (ev = timer_head(&tl->ordered);
1286 ev;
1287 ev = timer_next(&tl->ordered, ev)) {
1288 (void)talloc_get_type_abort(ev, fr_timer_t);
1289 TIMER_DUMP(ev);
1290 }
1291 break;
1292 }
1293}
1294#endif
int const char * file
Definition acutest.h:702
int const char int line
Definition acutest.h:702
#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:428
#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:139
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:156
#define FR_DLIST_TYPES(_name)
Define type specific wrapper structs for dlists.
Definition dlist.h:1129
#define FR_DLIST_FUNCS(_name, _element_type, _element_entry)
Define type specific wrapper functions for dlists.
Definition dlist.h:1152
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition dlist.h:275
#define FR_DLIST_TYPEDEFS(_name, _head, _entry)
Define friendly names for type specific dlist head and entry structures.
Definition dlist.h:1139
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(reap)
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:80
fr_lst_index_t fr_lst_iter_t
Definition lst.h:45
unsigned int fr_lst_index_t
Definition lst.h:43
unsigned int uint32_t
static bool done
Definition radclient.c:81
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition rb.c:824
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition rb.c:850
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
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#define fr_rb_inline_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black tree.
Definition rb.h:271
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_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:81
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:830
static uint64_t timer_list_ordered_num_events(fr_timer_list_t *tl)
Definition timer.c:953
static fr_timer_list_t * timer_list_alloc(TALLOC_CTX *ctx, fr_timer_list_t *parent)
Definition timer.c:1038
int fr_timer_list_disarm(fr_timer_list_t *tl)
Disable all timers in a list.
Definition timer.c:961
int(* timer_disarm_t)(fr_timer_t *ev)
Specialisation function to delete a timer.
Definition timer.c:126
timer_head_t deferred
A list of timer events to be inserted, after the current batch has been processed.
Definition timer.c:59
fr_time_t fr_timer_when(fr_timer_t *ev)
Internal timestamp representing when the timer should fire.
Definition timer.c:666
uint64_t(* timer_list_num_elements_t)(fr_timer_list_t *tl)
Return the number of elements in the list.
Definition timer.c:162
timer_list_num_elements_t num_events
Function to get the number of elements in the list.
Definition timer.c:171
timer_list_type_t type
Definition timer.c:56
uint64_t fr_timer_list_num_events(fr_timer_list_t *tl)
Return number of pending events.
Definition timer.c:980
fr_timer_t ** parent
A pointer to the parent structure containing the timer event.
Definition timer.c:83
static int timer_list_lst_deferred(fr_timer_list_t *tl)
Insert a timer event into a the lst.
Definition timer.c:894
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:240
static int _timer_free(fr_timer_t *ev)
Remove an event from the event loop.
Definition timer.c:337
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:312
fr_time_t fr_timer_list_when(fr_timer_list_t *tl)
Return the time of the next event.
Definition timer.c:994
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:1091
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:292
bool _fr_timer_armed(fr_timer_t *ev)
Check if a timer event is armed.
Definition timer.c:678
fr_timer_t *(* timer_list_head_t)(fr_timer_list_t *tl)
Return the soonest timer event.
Definition timer.c:145
timer_disarm_t disarm
Function to delete a timer event.
Definition timer.c:166
static int _timer_list_free(fr_timer_list_t *tl)
Cleanup all timers currently in the list.
Definition timer.c:1020
void const * uctx
Context pointer to pass to the callback.
Definition timer.c:79
int(* timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when)
Specialisation function to execute any pending timers.
Definition timer.c:136
timer_list_head_t head
Function to get the head of the list.
Definition timer.c:169
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:227
int fr_timer_list_force_run(fr_timer_list_t *tl)
Get the head of the timer list, the event may not be ready to fire.
Definition timer.c:812
fr_timer_cb_t callback
Callback to execute when the timer fires.
Definition timer.c:78
static int timer_lst_disarm(fr_timer_t *ev)
Definition timer.c:540
int(* timer_list_deferred_t)(fr_timer_list_t *tl)
Process any deferred timer events.
Definition timer.c:154
fr_time_t when
When this timer should fire.
Definition timer.c:76
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:530
static int timer_list_parent_update(fr_timer_list_t *tl)
Utility function to update parent timers.
Definition timer.c:256
timer_insert_t insert
Function to insert a timer event.
Definition timer.c:165
fr_timer_t * parent_ev
Event in the parent's event loop.
Definition timer.c:65
bool free_on_fire
Whether to free the event when it fires.
Definition timer.c:91
fr_timer_entry_t entry
Entry in a list of timer events.
Definition timer.c:86
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:694
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:1065
static timer_list_funcs_t const timer_funcs[]
Functions for performing operations on various types of timer list.
Definition timer.c:197
static int timer_ordered_disarm(fr_timer_t *ev)
Remove a timer from a timer list, but don't free it.
Definition timer.c:576
int fr_timer_disarm(fr_timer_t *ev)
Remove an event from the event list, but don't free the memory.
Definition timer.c:602
fr_timer_list_t * parent
Parent list to insert event into (if any).
Definition timer.c:64
#define CHECK_PARENT(_ev)
Definition timer.c:105
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:753
static fr_timer_t * timer_list_lst_head(fr_timer_list_t *tl)
Return the head of the event list.
Definition timer.c:870
static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl)
Definition timer.c:948
timer_list_type_t
What type of event list the timer is inserted into.
Definition timer.c:41
@ 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:99
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:1008
timer_list_deferred_t deferred
Function to process deferred events.
Definition timer.c:170
static fr_timer_t * timer_list_ordered_head(fr_timer_list_t *tl)
Return the head of the ordered list.
Definition timer.c:882
bool in_handler
Whether we're currently in a callback.
Definition timer.c:57
#define EVENT_ARMED(_ev)
Definition timer.c:174
int fr_timer_delete(fr_timer_t **ev_p)
Delete a timer event and free its memory.
Definition timer.c:639
char const * file
Source file this event was last updated in.
Definition timer.c:98
fr_timer_list_t * tl
The event list this timer is part of.
Definition timer.c:93
static int timer_list_ordered_deferred(fr_timer_list_t *tl)
Move all deferred events into the ordered event list.
Definition timer.c:917
int(* timer_insert_t)(fr_timer_list_t *tl, fr_timer_t *ev)
Specialisation function to insert a timer.
Definition timer.c:117
timer_list_run_t run
Function to run a timer event.
Definition timer.c:168
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:371
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:75
#define FR_TIMER_DISARM_RETURN(_ev)
Definition timer.h:97
#define fr_timer_in(...)
Definition timer.h:86
fr_time_t(* fr_event_time_source_t)(void)
Alternative time source, useful for testing.
Definition timer.h:51
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:74
#define fr_timer_at(...)
Definition timer.h:80
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:60
Public event timer list structure.
Definition timer.h:59
static fr_slen_t head
Definition xlat.h:416
static fr_slen_t parent
Definition pair.h:845
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