Coverage Report

Created: 2026-05-11 06:44

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 {
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
/** Specialisation function to insert a timer
120
 *
121
 * @param[in] tl  Timer list to insert into.
122
 * @param[in] ev  Timer event to insert.
123
 * @return
124
 *  - 0 on success.
125
 *  - -1 on failure.
126
 */
127
typedef int (*timer_insert_t)(fr_timer_list_t *tl, fr_timer_t *ev);
128
129
/** Specialisation function to delete a timer
130
 *
131
 * @param[in] ev  Timer event to delete.
132
 * @return
133
 *  - 0 on success.
134
 *  - -1 on failure.
135
 */
136
typedef int (*timer_disarm_t)(fr_timer_t *ev);
137
138
/** Specialisation function to execute any pending timers
139
 *
140
 * @param[in] tl  Timer list to execute.
141
 * @param[in,out] when  Our current time, updated to the next event time (i.e. the next time we'll need to run something)
142
 * @return
143
 *  - 0 no timer events fired.
144
 *  - 1 a timer event fired.
145
 */
146
typedef int (*timer_list_run_t)(fr_timer_list_t *tl, fr_time_t *when);
147
148
/** Return the soonest timer event
149
 *
150
 * @param[in] tl  to get the head of.
151
 * @return
152
 *  - The head of the list.
153
 *  - NULL if the list is empty.
154
 */
155
typedef fr_timer_t *(*timer_list_head_t)(fr_timer_list_t *tl);
156
157
/** Process any deferred timer events
158
 *
159
 * @param[in] tl  to process deferred events for.
160
 * @return
161
 *  - The head of the list.
162
 *  - NULL if the list is empty.
163
 */
164
typedef int (*timer_list_deferred_t)(fr_timer_list_t *tl);
165
166
/** Return the number of elements in the list
167
 *
168
 * @param[in] tl  to get the number of elements from.
169
 * @return
170
 *  - The number of elements in the list.
171
 */
172
typedef uint64_t (*timer_list_num_elements_t)(fr_timer_list_t *tl);
173
174
typedef struct {
175
  timer_insert_t      insert;   //!< Function to insert a timer event.
176
  timer_disarm_t      disarm;   //!< Function to delete a timer event.
177
178
  timer_list_run_t    run;    //!< Function to run a timer event.
179
  timer_list_head_t   head;   //!< Function to get the head of the list.
180
  timer_list_deferred_t   deferred; //!< Function to process deferred events.
181
  timer_list_num_elements_t num_events; //!< Function to get the number of elements in the list.
182
} timer_list_funcs_t;
183
184
0
#define EVENT_ARMED(_ev) ((_ev)->tl != NULL)
185
186
static fr_time_t *timer_list_when(fr_timer_list_t *tl);
187
188
static int timer_lst_insert_at(fr_timer_list_t *tl, fr_timer_t *ev);
189
static int timer_ordered_insert_at(fr_timer_list_t *tl, fr_timer_t *ev);
190
191
static int timer_lst_disarm(fr_timer_t *ev);
192
static int timer_ordered_disarm(fr_timer_t *ev);
193
194
static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when);
195
static int timer_list_ordered_run(fr_timer_list_t *tl, fr_time_t *when)
196
;static int timer_list_shared_run(fr_timer_list_t *tl, fr_time_t *when);
197
198
static fr_timer_t *timer_list_lst_head(fr_timer_list_t *tl);
199
static fr_timer_t *timer_list_ordered_head(fr_timer_list_t *tl);
200
201
static int timer_list_lst_deferred(fr_timer_list_t *tl);
202
static int timer_list_ordered_deferred(fr_timer_list_t *tl);
203
static int timer_list_shared_deferred(fr_timer_list_t *tl);
204
205
static uint64_t timer_list_lst_num_events(fr_timer_list_t *tl);
206
static uint64_t timer_list_ordered_num_events(fr_timer_list_t *tl);
207
static uint64_t timer_list_shared_num_events(fr_timer_list_t *tl);
208
209
/** Functions for performing operations on various types of timer list
210
 *
211
 */
212
static timer_list_funcs_t const timer_funcs[] = {
213
  [TIMER_LIST_TYPE_LST] = {
214
    .insert = timer_lst_insert_at,
215
    .disarm = timer_lst_disarm,
216
217
    .run = timer_list_lst_run,
218
    .head = timer_list_lst_head,
219
    .deferred = timer_list_lst_deferred,
220
    .num_events = timer_list_lst_num_events
221
  },
222
  [TIMER_LIST_TYPE_ORDERED] = {
223
    .insert = timer_ordered_insert_at,
224
    .disarm = timer_ordered_disarm,
225
226
    .run = timer_list_ordered_run,
227
    .head = timer_list_ordered_head,
228
    .deferred = timer_list_ordered_deferred,
229
    .num_events = timer_list_ordered_num_events
230
  },
231
  [TIMER_LIST_TYPE_SHARED] = {
232
//    .insert = timer_shared_insert_at,
233
//    .disarm = timer_shared_disarm,
234
235
    .run = timer_list_shared_run,
236
//    .head = timer_list_shared_head,
237
    .deferred = timer_list_shared_deferred,
238
    .num_events = timer_list_shared_num_events
239
  },
240
};
241
242
/** Compare two timer events to see which one should occur first
243
 *
244
 * @param[in] a the first timer event.
245
 * @param[in] b the second timer event.
246
 * @return
247
 *  - +1 if a should occur later than b.
248
 *  - -1 if a should occur earlier than b.
249
 *  - 0 if both events occur at the same time.
250
 */
251
static int8_t timer_cmp(void const *a, void const *b)
252
0
{
253
0
  fr_timer_t const *ev_a = a, *ev_b = b;
254
255
0
  return fr_time_cmp(ev_a->when, ev_b->when);
256
0
}
257
258
259
/** This callback fires in the parent to execute events in this sublist
260
 *
261
 * @param[in] parent_tl Parent event timer list.
262
 * @param[in] when    When the parent timer fired.
263
 * @param[in] uctx    Sublist to execute.
264
 */
265
static void _parent_timer_cb(UNUSED fr_timer_list_t *parent_tl, fr_time_t when, void *uctx)
266
0
{
267
0
  fr_timer_list_t *tl = talloc_get_type_abort(uctx, fr_timer_list_t);
268
269
0
  fr_assert(!tl->disarmed);
270
271
  /*
272
   *  We're in the parent timer, so we need to run the
273
   *  events in the child timer list.
274
   */
275
0
  (void)fr_timer_list_run(tl, &when);
276
0
}
277
278
/** Utility function to update parent timers
279
 *
280
 * @param[in] tl  to update parent timers for.
281
 * @return
282
 *  - 0 on success.
283
 *  - -1 on failure.
284
 */
285
static inline CC_HINT(always_inline) int timer_list_parent_update(fr_timer_list_t *tl)
286
0
{
287
0
  fr_time_t *when;
288
289
0
  if (!tl->parent) return 0;
290
291
0
  when = timer_list_when(tl);
292
293
  /*
294
   *  No events, disarm the timer
295
   */
296
0
  if (!when) {
297
    /*
298
     *  Disables the timer in the parent, does not free the memory
299
     */
300
0
    if (tl->parent) FR_TIMER_DISARM_RETURN(tl->parent_ev);
301
0
    return 0;
302
0
  }
303
304
  /*
305
   *  We have an active event, we can suppress changes which have no effect.
306
   */
307
0
  if (tl->parent_ev && EVENT_ARMED(tl->parent_ev)) {
308
0
    fr_assert(!tl->disarmed); /* fr_timer_list_disarm() must disarm it */
309
310
0
    if (fr_time_eq(*when, tl->parent_ev->when)) {
311
0
      return 0;
312
0
    }
313
0
  }
314
315
  /*
316
   *  This is a child list which is disabled.  Don't update the parent.
317
   */
318
0
  if (tl->disarmed) {
319
0
    fr_assert(tl->parent);
320
321
0
    fr_assert(!tl->parent_ev || !EVENT_ARMED(tl->parent_ev));
322
0
    return 0;
323
0
  }
324
325
  /*
326
   *  The list is armed and the head has changed, so we change the event in the parent list.
327
   */
328
0
  if (fr_timer_at(tl, tl->parent, &tl->parent_ev,
329
0
           *when, false, _parent_timer_cb, tl) < 0) return -1;
330
331
0
  return 0;
332
0
}
333
334
/** Insert a timer event into a single event timer list
335
 *
336
 * @param[in] tl  to insert the event into.
337
 * @param[in] ev  to insert.
338
 * @return
339
 *  - 0 on success.
340
 *  - -1 on failure.
341
 */
342
static int timer_lst_insert_at(fr_timer_list_t *tl, fr_timer_t *ev)
343
0
{
344
0
  if (unlikely(fr_lst_insert(tl->lst, ev) < 0)) {
345
0
    fr_strerror_const_push("Failed inserting timer into lst");
346
0
    return -1;
347
0
  }
348
349
0
  return 0;
350
0
}
351
352
/** Insert an event into an ordered timer list
353
 *
354
 * Timer must be in order, i.e. either before first event, or after last event
355
 *
356
 * @param[in] tl  to insert the event into.
357
 * @param[in] ev  to insert.
358
 * @return
359
 *  - 0 on success.
360
 *  - -1 on failure.
361
 */
362
static int timer_ordered_insert_at(fr_timer_list_t *tl, fr_timer_t *ev)
363
0
{
364
0
  fr_timer_t  *tail;
365
366
0
  tail = timer_tail(&tl->ordered);
367
0
  if (tail && fr_time_lt(ev->when, tail->when)) {
368
0
    fr_strerror_const("Event being inserted must occurr _after_ the last event");
369
0
    return -1;
370
0
  }
371
372
0
  if (unlikely(timer_insert_tail(&tl->ordered, ev) < 0)) {
373
0
    fr_strerror_const_push("Failed inserting timer into ordered list");
374
0
    return -1;
375
0
  }
376
377
0
  return 0;
378
0
}
379
380
/** Remove an event from the event loop
381
 *
382
 * @param[in] ev  to free.
383
 * @return
384
 *  - 0 on success.
385
 *  - -1 on failure.
386
 */
387
static int _timer_free(fr_timer_t *ev)
388
0
{
389
0
  fr_timer_t  **ev_p;
390
0
  int   ret;
391
392
0
  ret = fr_timer_disarm(ev);  /* Is a noop if ev->tl == NULL */
393
0
  if (ret < 0) return ret;
394
395
0
  CHECK_PARENT(ev);
396
0
  ev_p = ev->parent;
397
0
  *ev_p = NULL;
398
399
0
  return 0;
400
0
}
401
402
/** Insert a timer event into an event list
403
 *
404
 * @note The talloc parent of the memory returned in ev_p must not be changed.
405
 *   If the lifetime of the event needs to be bound to another context
406
 *   this function should be called with the existing event pointed to by
407
 *   ev_p.
408
 *
409
 * @param[in] ctx   to bind lifetime of the event to.
410
 * @param[in] tl    to insert event into.
411
 * @param[in,out] ev_p    If not NULL modify this event instead of creating a new one.  This is a parent
412
 *        in a temporal sense, not in a memory structure or dependency sense.
413
 * @param[in] when    we should run the event.
414
 * @param[in] free_on_fire  Whether event memory should be freed if the event fires.
415
 * @param[in] callback    function to execute if the event fires.
416
 * @param[in] uctx    user data to pass to the event.
417
 * @return
418
 *  - 0 on success.
419
 *  - -1 on failure.
420
 */
421
int _fr_timer_at(NDEBUG_LOCATION_ARGS
422
     TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
423
     fr_time_t when,
424
     bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
425
0
{
426
0
  fr_timer_t *ev;
427
428
0
  fr_assert(tl->type != TIMER_LIST_TYPE_SHARED);
429
430
  /*
431
   *  If there is an event, reuse it instead of freeing it
432
   *  and allocating a new one.  This is to reduce memory
433
   *  churn for repeat events.
434
   */
435
0
  if (!*ev_p) {
436
0
  new_event:
437
0
    ev = talloc_zero(tl, fr_timer_t);
438
0
    if (unlikely(!ev)) {
439
0
      fr_strerror_const("Out of memory");
440
0
      return -1;
441
0
    }
442
443
0
    EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Added new timer %p", tl, NDEBUG_LOCATION_VALS ev);
444
    /*
445
     *  Bind the lifetime of the event to the specified
446
     *  talloc ctx.  If the talloc ctx is freed, the
447
     *  event will also be freed.
448
     */
449
0
    if (ctx != tl) talloc_link_ctx(ctx, ev);
450
451
0
    talloc_set_destructor(ev, _timer_free);
452
0
  } else {
453
0
    ev = talloc_get_type_abort(UNCONST(fr_timer_t *, *ev_p), fr_timer_t);
454
455
0
    EVENT_DEBUG("%p - " NDEBUG_LOCATION_FMT "Re-armed timer %p", tl, NDEBUG_LOCATION_VALS ev);
456
457
    /*
458
     *  We can't disarm the linking context due to
459
     *  limitations in talloc, so if the linking
460
     *  context changes, we need to free the old
461
     *  event, and allocate a new one.
462
     *
463
     *  Freeing the event also removes it from the lst.
464
     */
465
0
    if (unlikely(ev->linked_ctx != ctx)) {
466
0
      talloc_free(ev);
467
0
      goto new_event;
468
0
    }
469
470
    /*
471
     *  If the event is associated with a list, we need
472
     *  to disarm it, before we can rearm it.
473
     */
474
0
    if (EVENT_ARMED(ev)) {
475
0
      int   ret;
476
0
      char const  *err_file;
477
0
      int   err_line;
478
479
      /*
480
       *  Removed event from the event list or the
481
       *  deferred list.
482
       */
483
0
      ret = fr_timer_disarm(ev);
484
0
#ifndef NDEBUG
485
0
      err_file = ev->file;
486
0
      err_line = ev->line;
487
#else
488
      err_file = "not-available";
489
      err_line = 0;
490
#endif
491
492
      /*
493
       *  Events MUST be in the lst (or the insertion list).
494
       */
495
0
      if (!fr_cond_assert_msg(ret == 0,
496
0
            "Event %p, allocd %s[%d], was not found in the event "
497
0
            "list or deferred list when re-armed: %s", ev,
498
0
            err_file, err_line, fr_strerror())) return -1;
499
0
    }
500
0
  }
501
502
0
  ev->tl = tl;  /* This indicates the event memory is bound to an event loop */
503
0
  ev->when = when;
504
0
  ev->free_on_fire = free_on_fire;
505
0
  ev->callback = callback;
506
0
  ev->uctx = uctx;
507
0
  ev->linked_ctx = ctx;
508
0
  ev->parent = ev_p;
509
0
#ifndef NDEBUG
510
0
  ev->file = file;
511
0
  ev->line = line;
512
0
#endif
513
514
  /*
515
   *  No updating needed as the events are deferred
516
   */
517
0
  if (tl->in_handler) {
518
    /*
519
     *  ...a little hacky, but we need to verify that
520
     *  we're not inserting an event that's earlier
521
     *  than the last event in the list for ordered
522
     *  lists.
523
     *
524
     *  Otherwise we'd end up doing this when we tried
525
     *  to move all the deferred events into the timer
526
     *  list, and end up making that O(n) instead of O(1).
527
     */
528
0
    if (tl->type == TIMER_LIST_TYPE_ORDERED) {
529
0
      fr_timer_t *head = timer_list_ordered_head(tl);
530
531
0
      if (head && fr_time_lt(ev->when, head->when)) {
532
0
        fr_strerror_const("Event being inserted must occurr _after_ the last event");
533
534
0
      insert_failed:
535
0
        talloc_set_destructor(ev, NULL);
536
0
        talloc_free(ev);
537
0
        *ev_p = NULL;
538
0
        return -1;
539
0
      }
540
0
    }
541
542
0
    if (!fr_cond_assert_msg(timer_insert_tail(&tl->deferred, ev) == 0,
543
0
          "Failed inserting event into deferred list")) {
544
0
      goto insert_failed;
545
0
    }
546
0
  } else {
547
0
    int ret;
548
549
0
    ret = timer_funcs[tl->type].insert(tl, ev);
550
0
    if (unlikely(ret < 0)) goto insert_failed;
551
552
    /*
553
     *  We need to update the parent timer
554
     *  to ensure it fires at the correct time.
555
     */
556
0
    if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
557
0
  }
558
559
0
  *ev_p = ev;
560
561
0
  return 0;
562
0
}
563
564
/** Insert a timer event into an event list
565
 *
566
 * @note The talloc parent of the memory returned in ev_p must not be changed.
567
 *   If the lifetime of the event needs to be bound to another context
568
 *   this function should be called with the existing event pointed to by
569
 *   ev_p.
570
 *
571
 * @param[in] ctx   to bind lifetime of the event to.
572
 * @param[in] tl    to insert event into.
573
 * @param[in,out] ev_p    If not NULL modify this event instead of creating a new one.  This is a parent
574
 *        in a temporal sense, not in a memory structure or dependency sense.
575
 * @param[in] delta   In how many nanoseconds to wait before should we execute the event.
576
 * @param[in] free_on_fire  Whether event memory should be freed if the event fires.
577
 * @param[in] callback    function to execute if the event fires.
578
 * @param[in] uctx    user data to pass to the event.
579
 * @return
580
 *  - 0 on success.
581
 *  - -1 on failure.
582
 */
583
int _fr_timer_in(NDEBUG_LOCATION_ARGS
584
     TALLOC_CTX *ctx, fr_timer_list_t *tl, fr_timer_t **ev_p,
585
     fr_time_delta_t delta,
586
     bool free_on_fire, fr_timer_cb_t callback, void const *uctx)
587
0
{
588
0
  return _fr_timer_at(NDEBUG_LOCATION_VALS
589
0
          ctx, tl, ev_p, fr_time_add(tl->pub.time(), delta),
590
0
          free_on_fire, callback, uctx);
591
0
}
592
593
static int timer_lst_disarm(fr_timer_t *ev)
594
{
595
  fr_timer_list_t *tl = ev->tl;
596
597
  if (timer_in_list(&tl->deferred,ev)) {
598
    (void)timer_remove(&tl->deferred, ev);
599
  } else {
600
    int   ret = fr_lst_extract(tl->lst, ev);
601
    char const  *err_file;
602
    int   err_line;
603
604
#ifndef NDEBUG
605
    err_file = ev->file;
606
    err_line = ev->line;
607
#else
608
    err_file = "not-available";
609
    err_line = 0;
610
#endif
611
612
613
    /*
614
     *  Events MUST be in the lst (or the insertion list).
615
    */
616
    if (!fr_cond_assert_msg(ret == 0,
617
          "Event %p, lst_id %u, allocd %s[%d], was not found in the event lst or "
618
          "insertion list when freed: %s", ev, ev->lst_idx, err_file, err_line,
619
          fr_strerror())) return -1;
620
  }
621
622
  return 0;
623
}
624
625
/** Remove a timer from a timer list, but don't free it
626
 *
627
 * @param[in] ev to remove.
628
 */
629
static int timer_ordered_disarm(fr_timer_t *ev)
630
{
631
  /*
632
   *  Check the check is still valid (sanity check)
633
   */
634
  (void)talloc_get_type_abort(ev, fr_timer_t);;
635
636
  /*
637
   *  Already dissassociated from a list, nothing to do.
638
   */
639
  if (!ev->tl) return 0;
640
641
  /*
642
   *  This *MUST* be in a timer list if it has a non-NULL tl pointer.
643
   */
644
  if (unlikely(!fr_cond_assert(timer_in_list(&ev->tl->ordered, ev)))) return -1;
645
646
  (void)timer_remove(&ev->tl->ordered, ev);
647
648
  return 0;
649
}
650
651
/** Remove an event from the event list, but don't free the memory
652
 *
653
 * @param[in] ev  to remove from the event list.
654
 */
655
int fr_timer_disarm(fr_timer_t *ev)
656
0
{
657
0
  fr_timer_list_t *tl;
658
659
0
  if (!ev || !EVENT_ARMED(ev)) {
660
0
    EVENT_DEBUG("Asked to disarm inactive timer %p (noop)", ev);
661
0
    return 0; /* Noop */
662
0
  }
663
664
0
  tl = ev->tl;
665
666
0
  EVENT_DEBUG("Disarming timer %p", ev);
667
668
0
  CHECK_PARENT(ev);
669
670
  /*
671
   *  If the event is deferred, it's not in the event list proper
672
   *  so just remove it, and set the tl pointer to NULL.
673
   */
674
0
  if (timer_in_list(&tl->deferred,ev)) {
675
0
    (void)timer_remove(&tl->deferred, ev);
676
0
  } else {
677
0
    int ret = timer_funcs[ev->tl->type].disarm(ev);
678
0
    if (ret < 0) return ret;
679
0
  }
680
0
  ev->tl = NULL;
681
682
0
  return timer_list_parent_update(tl);
683
0
}
684
685
/** Delete a timer event and free its memory
686
 *
687
 * @param[in] ev_p  of the event being deleted.
688
 * @return
689
 *  - 0 on success.
690
 *  - -1 on failure.
691
 */
692
int fr_timer_delete(fr_timer_t **ev_p)
693
0
{
694
0
  fr_timer_t *ev;
695
0
  int ret;
696
697
0
  if (unlikely(!*ev_p)) return 0;
698
699
0
  ev = *ev_p;
700
0
  ret = talloc_free(ev);  /* Destructor removed event from any lists */
701
702
  /*
703
   *  Don't leave a garbage pointer value
704
   *  if parent is not ev_p.
705
   */
706
0
  if (likely(ret == 0)) {
707
0
    *ev_p = NULL;
708
0
    return 0;
709
0
  }
710
711
0
  EVENT_DEBUG("Deleting timer %p failed: %s", ev, fr_strerror_peek());
712
0
  return -1;
713
0
}
714
715
/** Internal timestamp representing when the timer should fire
716
 *
717
 * @return When the timestamp should fire.
718
 */
719
fr_time_t fr_timer_when(fr_timer_t *ev)
720
0
{
721
0
  if (!fr_timer_armed(ev)) return fr_time_wrap(0);
722
0
  return ev->when;
723
0
}
724
725
/** Return time delta between now and when the timer should fire
726
 *
727
 * @param[in] ev to get the time delta for.
728
 */
729
fr_time_delta_t fr_timer_remaining(fr_timer_t *ev)
730
0
{
731
0
  if (!fr_timer_armed(ev)) return fr_time_delta_wrap(0);
732
0
  return fr_time_sub(ev->when, ev->tl->pub.time());
733
0
}
734
735
/** Check if a timer event is armed
736
 *
737
 * @param[in] ev to check.
738
 * @return
739
 *  - true if the event is armed.
740
 *  - false if the event is not armed.
741
 */
742
bool _fr_timer_armed(fr_timer_t *ev)
743
0
{
744
0
  return EVENT_ARMED(ev);
745
0
}
746
747
/** Run all scheduled timer events in a lst
748
 *
749
 * @param[in] tl  containing the timer events.
750
 * @param[in] when  Process events scheduled to run before or at this time.
751
 *      - Set to 0 if no more events.
752
 *      - Set to the next event time if there are more events.
753
 * @return
754
 *  - 0 no timer events fired.
755
 *  - 1 a timer event fired.
756
 */
757
CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
758
static int timer_list_lst_run(fr_timer_list_t *tl, fr_time_t *when)
759
0
{
760
0
  fr_timer_cb_t callback;
761
0
  void    *uctx;
762
0
  fr_timer_t  *ev;
763
0
  int   fired = 0;
764
765
0
  while (fr_lst_num_elements(tl->lst) > 0) {
766
0
    ev = talloc_get_type_abort(fr_lst_peek(tl->lst), fr_timer_t);
767
768
    /*
769
     *  See if it's time to do this one.
770
     */
771
0
    if (fr_time_gt(ev->when, *when)) {
772
0
      *when = ev->when;
773
0
    done:
774
0
      return fired;
775
0
    }
776
777
0
    callback = ev->callback;
778
0
    memcpy(&uctx, &ev->uctx, sizeof(uctx));
779
780
0
    CHECK_PARENT(ev);
781
782
    /*
783
     *  Disarm the event before calling it.
784
     *
785
     *  This leaves the memory in place,
786
     *  but dissassociates it from the list.
787
     *
788
     *  We use the public function as it
789
     *  handles more cases.
790
     */
791
0
    if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
792
0
    EVENT_DEBUG("Running timer %p", ev);
793
0
    if (ev->free_on_fire) talloc_free(ev);
794
795
0
    callback(tl, *when, uctx);
796
797
0
    fired++;
798
0
  }
799
800
0
  *when = fr_time_wrap(0);
801
802
0
  goto done;
803
0
}
804
805
/** Run all scheduled events in an ordered list
806
 *
807
 * @param[in] tl  containing the timer events.
808
 * @param[in] when  Process events scheduled to run before or at this time.
809
 *      - Set to 0 if no more events.
810
 *      - Set to the next event time if there are more events.
811
 * @return
812
 *  - < 0 if we failed to updated the parent list.
813
 *  - 0 no timer events fired.
814
 *  - >0 number of timer event fired.
815
 */
816
CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
817
static int timer_list_ordered_run(fr_timer_list_t *tl, fr_time_t *when)
818
0
{
819
0
  fr_timer_cb_t callback;
820
0
  void    *uctx;
821
0
  fr_timer_t  *ev;
822
0
  unsigned int  fired = 0;
823
824
0
  while ((ev = timer_head(&tl->ordered))) {
825
0
    (void)talloc_get_type_abort(ev, fr_timer_t);
826
827
    /*
828
     *  See if it's time to do this one.
829
     */
830
0
    if (fr_time_gt(ev->when, *when)) {
831
0
      *when = ev->when;
832
0
    done:
833
0
      return fired;
834
0
    }
835
836
0
    callback = ev->callback;
837
0
    memcpy(&uctx, &ev->uctx, sizeof(uctx));
838
839
0
    CHECK_PARENT(ev);
840
841
    /*
842
     *  Disarm the event before calling it.
843
     *
844
     *  This leaves the memory in place,
845
     *  but dissassociates it from the list.
846
     *
847
     *  We use the public function as it
848
     *  handles more cases.
849
     */
850
0
    if (!fr_cond_assert(fr_timer_disarm(ev) == 0)) return -2;
851
0
    EVENT_DEBUG("Running timer %p", ev);
852
0
    if (ev->free_on_fire) talloc_free(ev);
853
854
0
    callback(tl, *when, uctx);
855
856
0
    fired++;
857
0
  }
858
859
0
  *when = fr_time_wrap(0);
860
861
0
  goto done;
862
0
}
863
864
/** Run all scheduled events in an ordered list
865
 *
866
 * @param[in] tl  containing the timer events.
867
 * @param[in] when  Process events scheduled to run before or at this time.
868
 *      - Set to 0 if no more events.
869
 *      - Set to the next event time if there are more events.
870
 * @return
871
 *  - < 0 if we failed to updated the parent list.
872
 *  - 0 no timer events fired.
873
 *  - >0 number of timer event fired.
874
 */
875
CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private fr_timer_list_t trips --fsanitize=function*/
876
static int timer_list_shared_run(fr_timer_list_t *tl, fr_time_t *when)
877
0
{
878
0
  void    *uctx;
879
0
  unsigned int  fired = 0;
880
881
0
  while ((uctx = fr_rb_first(tl->shared.rb)) != NULL) {
882
0
    fr_time_t const *next;
883
884
0
    next = TIMER_UCTX_TO_TIME(tl, uctx);
885
886
    /*
887
     *  See if it's time to do this one.
888
     */
889
0
    if (fr_time_gt(*next, *when)) {
890
0
      *when = *next;
891
0
    done:
892
0
      return fired;
893
0
    }
894
895
0
    fr_rb_remove(tl->shared.rb, uctx);
896
897
0
    tl->shared.callback(tl, *when, uctx);
898
899
0
    fired++;
900
0
  }
901
902
0
  *when = fr_time_wrap(0);
903
904
0
  goto done;
905
0
}
906
907
908
/** Forcibly run all events in an event loop.
909
 *
910
 * This is used to forcefully run every event in the event loop.
911
 *
912
 * We pass in the real time, which may theoretically cause issues if timer
913
 * callbacks are checking...  But the uses of this function are very limited.
914
 *
915
 * @return
916
 *  - < 0 if we failed to update the parent list.
917
 *  - 0 no timer events fired.
918
 *  - > 0 number of timer event fired.
919
 */
920
int fr_timer_list_force_run(fr_timer_list_t *tl)
921
0
{
922
0
  fr_time_t when = fr_time_max();
923
924
0
  return fr_timer_list_run(tl, &when);
925
0
}
926
927
/** Execute any pending events in the event loop
928
 *
929
 * @param[in] tl  to execute events in.
930
 * @param[in] when  Process events scheduled to run before or at this time.
931
 *      - Set to 0 if no more events.
932
 *      - Set to the next event time if there are more events.
933
 * @return
934
 *  - < 0 if we failed to update the parent list.
935
 *  - 0 no timer events fired.
936
 *  - >0 number of timer event fired.
937
 */
938
int fr_timer_list_run(fr_timer_list_t *tl, fr_time_t *when)
939
0
{
940
0
  int ret;
941
0
  bool in_handler = tl->in_handler; /* allow nested timer execution */
942
943
0
  tl->in_handler = true;
944
0
  ret = timer_funcs[tl->type].run(tl, when);
945
0
  tl->in_handler = in_handler;
946
947
  /*
948
   *  Now we've executed all the pending events,
949
   *  now merge the deferred events into the main
950
   *  event list.
951
   *
952
   *  The events don't need to be modified as they
953
   *  were initialised completely before being
954
   *  placed in the deferred list.
955
   */
956
0
  if (timer_num_elements(&tl->deferred) > 0) {
957
0
    if (unlikely(timer_funcs[tl->type].deferred(tl) < 0)) return -1;
958
0
    if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
959
  /*
960
   *  We ran some events, and have no deferred
961
   *  events to insert, so we need to forcefully
962
   *  update the parent timer.
963
   */
964
0
  } else if(ret > 0) {
965
0
    if (unlikely(timer_list_parent_update(tl) < 0)) return -1;
966
0
  }
967
968
0
  return ret;
969
0
}
970
971
/** Return the head of the lst
972
 *
973
 * @param[in] tl  to get the head of.
974
 * @return
975
 *  - The head of the trie.
976
 *  - NULL, if there's no head.
977
 */
978
static fr_timer_t *timer_list_lst_head(fr_timer_list_t *tl)
979
0
{
980
0
  return fr_lst_peek(tl->lst);
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)) return -1;
1353
1354
0
    return 0;
1355
0
  }
1356
1357
0
  if (!fr_rb_insert(tl->shared.rb, uctx)) 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 int8_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
      counter = fr_rb_find(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