Coverage Report

Created: 2026-10-18 06:56

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