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