Coverage Report

Created: 2022-03-10 07:56

/src/bind9/lib/isc/timer.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <stdbool.h>
17
18
#include <isc/app.h>
19
#include <isc/condition.h>
20
#include <isc/heap.h>
21
#include <isc/log.h>
22
#include <isc/magic.h>
23
#include <isc/mem.h>
24
#include <isc/once.h>
25
#include <isc/print.h>
26
#include <isc/refcount.h>
27
#include <isc/task.h>
28
#include <isc/thread.h>
29
#include <isc/time.h>
30
#include <isc/timer.h>
31
#include <isc/util.h>
32
33
#include "timer_p.h"
34
35
#ifdef ISC_TIMER_TRACE
36
#define XTRACE(s)      fprintf(stderr, "%s\n", (s))
37
#define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
38
#define XTRACETIME(s, d) \
39
  fprintf(stderr, "%s %u.%09u\n", (s), (d).seconds, (d).nanoseconds)
40
#define XTRACETIME2(s, d, n)                                      \
41
  fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), (d).seconds, \
42
    (d).nanoseconds, (n).seconds, (n).nanoseconds)
43
#define XTRACETIMER(s, t, d)                                      \
44
  fprintf(stderr, "%s %p %u.%09u\n", (s), (t), (d).seconds, \
45
    (d).nanoseconds)
46
#else /* ifdef ISC_TIMER_TRACE */
47
#define XTRACE(s)
48
#define XTRACEID(s, t)
49
#define XTRACETIME(s, d)
50
#define XTRACETIME2(s, d, n)
51
#define XTRACETIMER(s, t, d)
52
#endif /* ISC_TIMER_TRACE */
53
54
0
#define TIMER_MAGIC    ISC_MAGIC('T', 'I', 'M', 'R')
55
#define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
56
57
struct isc_timer {
58
  /*! Not locked. */
59
  unsigned int magic;
60
  isc_timermgr_t *manager;
61
  isc_mutex_t lock;
62
  isc_refcount_t references;
63
  /*! Locked by timer lock. */
64
  isc_time_t idle;
65
  /*! Locked by manager lock. */
66
  isc_timertype_t type;
67
  isc_time_t expires;
68
  isc_interval_t interval;
69
  isc_task_t *task;
70
  isc_taskaction_t action;
71
  void *arg;
72
  unsigned int index;
73
  isc_time_t due;
74
  LINK(isc_timer_t) link;
75
};
76
77
0
#define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
78
#define VALID_MANAGER(m)    ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
79
80
struct isc_timermgr {
81
  /* Not locked. */
82
  unsigned int magic;
83
  isc_mem_t *mctx;
84
  isc_mutex_t lock;
85
  /* Locked by manager lock. */
86
  bool done;
87
  LIST(isc_timer_t) timers;
88
  unsigned int nscheduled;
89
  isc_time_t due;
90
  isc_condition_t wakeup;
91
  isc_thread_t thread;
92
  isc_heap_t *heap;
93
};
94
95
void
96
isc_timermgr_poke(isc_timermgr_t *manager);
97
98
static inline isc_result_t
99
0
schedule(isc_timer_t *timer, isc_time_t *now, bool signal_ok) {
100
0
  isc_timermgr_t *manager;
101
0
  isc_time_t due;
102
103
  /*!
104
   * Note: the caller must ensure locking.
105
   */
106
107
0
  REQUIRE(timer->type != isc_timertype_inactive);
108
109
0
  manager = timer->manager;
110
111
  /*
112
   * Compute the new due time.
113
   */
114
0
  if (timer->type != isc_timertype_once) {
115
0
    isc_result_t result = isc_time_add(now, &timer->interval, &due);
116
0
    if (result != ISC_R_SUCCESS) {
117
0
      return (result);
118
0
    }
119
0
    if (timer->type == isc_timertype_limited &&
120
0
        isc_time_compare(&timer->expires, &due) < 0)
121
0
    {
122
0
      due = timer->expires;
123
0
    }
124
0
  } else {
125
0
    if (isc_time_isepoch(&timer->idle)) {
126
0
      due = timer->expires;
127
0
    } else if (isc_time_isepoch(&timer->expires)) {
128
0
      due = timer->idle;
129
0
    } else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
130
0
    {
131
0
      due = timer->idle;
132
0
    } else {
133
0
      due = timer->expires;
134
0
    }
135
0
  }
136
137
  /*
138
   * Schedule the timer.
139
   */
140
141
0
  if (timer->index > 0) {
142
    /*
143
     * Already scheduled.
144
     */
145
0
    int cmp = isc_time_compare(&due, &timer->due);
146
0
    timer->due = due;
147
0
    switch (cmp) {
148
0
    case -1:
149
0
      isc_heap_increased(manager->heap, timer->index);
150
0
      break;
151
0
    case 1:
152
0
      isc_heap_decreased(manager->heap, timer->index);
153
0
      break;
154
0
    case 0:
155
      /* Nothing to do. */
156
0
      break;
157
0
    }
158
0
  } else {
159
0
    timer->due = due;
160
0
    isc_heap_insert(manager->heap, timer);
161
0
    manager->nscheduled++;
162
0
  }
163
164
0
  XTRACETIMER("schedule", timer, due);
165
166
  /*
167
   * If this timer is at the head of the queue, we need to ensure
168
   * that we won't miss it if it has a more recent due time than
169
   * the current "next" timer.  We do this either by waking up the
170
   * run thread, or explicitly setting the value in the manager.
171
   */
172
173
0
  if (timer->index == 1 && signal_ok) {
174
0
    XTRACE("signal (schedule)");
175
0
    SIGNAL(&manager->wakeup);
176
0
  }
177
178
0
  return (ISC_R_SUCCESS);
179
0
}
180
181
static inline void
182
0
deschedule(isc_timer_t *timer) {
183
0
  isc_timermgr_t *manager;
184
185
  /*
186
   * The caller must ensure locking.
187
   */
188
189
0
  manager = timer->manager;
190
0
  if (timer->index > 0) {
191
0
    bool need_wakeup = false;
192
0
    if (timer->index == 1) {
193
0
      need_wakeup = true;
194
0
    }
195
0
    isc_heap_delete(manager->heap, timer->index);
196
0
    timer->index = 0;
197
0
    INSIST(manager->nscheduled > 0);
198
0
    manager->nscheduled--;
199
0
    if (need_wakeup) {
200
0
      XTRACE("signal (deschedule)");
201
0
      SIGNAL(&manager->wakeup);
202
0
    }
203
0
  }
204
0
}
205
206
static void
207
0
destroy(isc_timer_t *timer) {
208
0
  isc_timermgr_t *manager = timer->manager;
209
210
  /*
211
   * The caller must ensure it is safe to destroy the timer.
212
   */
213
214
0
  LOCK(&manager->lock);
215
216
0
  (void)isc_task_purgerange(timer->task, timer, ISC_TIMEREVENT_FIRSTEVENT,
217
0
          ISC_TIMEREVENT_LASTEVENT, NULL);
218
0
  deschedule(timer);
219
220
0
  UNLINK(manager->timers, timer, link);
221
222
0
  UNLOCK(&manager->lock);
223
224
0
  isc_task_detach(&timer->task);
225
0
  isc_mutex_destroy(&timer->lock);
226
0
  timer->magic = 0;
227
0
  isc_mem_put(manager->mctx, timer, sizeof(*timer));
228
0
}
229
230
isc_result_t
231
isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
232
     const isc_time_t *expires, const isc_interval_t *interval,
233
     isc_task_t *task, isc_taskaction_t action, void *arg,
234
0
     isc_timer_t **timerp) {
235
0
  REQUIRE(VALID_MANAGER(manager));
236
0
  REQUIRE(task != NULL);
237
0
  REQUIRE(action != NULL);
238
239
0
  isc_timer_t *timer;
240
0
  isc_result_t result;
241
0
  isc_time_t now;
242
243
  /*
244
   * Create a new 'type' timer managed by 'manager'.  The timers
245
   * parameters are specified by 'expires' and 'interval'.  Events
246
   * will be posted to 'task' and when dispatched 'action' will be
247
   * called with 'arg' as the arg value.  The new timer is returned
248
   * in 'timerp'.
249
   */
250
0
  if (expires == NULL) {
251
0
    expires = isc_time_epoch;
252
0
  }
253
0
  if (interval == NULL) {
254
0
    interval = isc_interval_zero;
255
0
  }
256
0
  REQUIRE(type == isc_timertype_inactive ||
257
0
    !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
258
0
  REQUIRE(timerp != NULL && *timerp == NULL);
259
0
  REQUIRE(type != isc_timertype_limited ||
260
0
    !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
261
262
  /*
263
   * Get current time.
264
   */
265
0
  if (type != isc_timertype_inactive) {
266
0
    TIME_NOW(&now);
267
0
  } else {
268
    /*
269
     * We don't have to do this, but it keeps the compiler from
270
     * complaining about "now" possibly being used without being
271
     * set, even though it will never actually happen.
272
     */
273
0
    isc_time_settoepoch(&now);
274
0
  }
275
276
0
  timer = isc_mem_get(manager->mctx, sizeof(*timer));
277
278
0
  timer->manager = manager;
279
0
  isc_refcount_init(&timer->references, 1);
280
281
0
  if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
282
0
    result = isc_time_add(&now, interval, &timer->idle);
283
0
    if (result != ISC_R_SUCCESS) {
284
0
      isc_mem_put(manager->mctx, timer, sizeof(*timer));
285
0
      return (result);
286
0
    }
287
0
  } else {
288
0
    isc_time_settoepoch(&timer->idle);
289
0
  }
290
291
0
  timer->type = type;
292
0
  timer->expires = *expires;
293
0
  timer->interval = *interval;
294
0
  timer->task = NULL;
295
0
  isc_task_attach(task, &timer->task);
296
0
  timer->action = action;
297
  /*
298
   * Removing the const attribute from "arg" is the best of two
299
   * evils here.  If the timer->arg member is made const, then
300
   * it affects a great many recipients of the timer event
301
   * which did not pass in an "arg" that was truly const.
302
   * Changing isc_timer_create() to not have "arg" prototyped as const,
303
   * though, can cause compilers warnings for calls that *do*
304
   * have a truly const arg.  The caller will have to carefully
305
   * keep track of whether arg started as a true const.
306
   */
307
0
  DE_CONST(arg, timer->arg);
308
0
  timer->index = 0;
309
0
  isc_mutex_init(&timer->lock);
310
0
  ISC_LINK_INIT(timer, link);
311
0
  timer->magic = TIMER_MAGIC;
312
313
0
  LOCK(&manager->lock);
314
315
  /*
316
   * Note we don't have to lock the timer like we normally would because
317
   * there are no external references to it yet.
318
   */
319
320
0
  if (type != isc_timertype_inactive) {
321
0
    result = schedule(timer, &now, true);
322
0
  } else {
323
0
    result = ISC_R_SUCCESS;
324
0
  }
325
0
  if (result == ISC_R_SUCCESS) {
326
0
    *timerp = timer;
327
0
    APPEND(manager->timers, timer, link);
328
0
  }
329
330
0
  UNLOCK(&manager->lock);
331
332
0
  if (result != ISC_R_SUCCESS) {
333
0
    timer->magic = 0;
334
0
    isc_mutex_destroy(&timer->lock);
335
0
    isc_task_detach(&timer->task);
336
0
    isc_mem_put(manager->mctx, timer, sizeof(*timer));
337
0
    return (result);
338
0
  }
339
340
0
  return (ISC_R_SUCCESS);
341
0
}
342
343
isc_result_t
344
isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
345
    const isc_time_t *expires, const isc_interval_t *interval,
346
0
    bool purge) {
347
0
  isc_time_t now;
348
0
  isc_timermgr_t *manager;
349
0
  isc_result_t result;
350
351
  /*
352
   * Change the timer's type, expires, and interval values to the given
353
   * values.  If 'purge' is true, any pending events from this timer
354
   * are purged from its task's event queue.
355
   */
356
357
0
  REQUIRE(VALID_TIMER(timer));
358
0
  manager = timer->manager;
359
0
  REQUIRE(VALID_MANAGER(manager));
360
361
0
  if (expires == NULL) {
362
0
    expires = isc_time_epoch;
363
0
  }
364
0
  if (interval == NULL) {
365
0
    interval = isc_interval_zero;
366
0
  }
367
0
  REQUIRE(type == isc_timertype_inactive ||
368
0
    !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
369
0
  REQUIRE(type != isc_timertype_limited ||
370
0
    !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
371
372
  /*
373
   * Get current time.
374
   */
375
0
  if (type != isc_timertype_inactive) {
376
0
    TIME_NOW(&now);
377
0
  } else {
378
    /*
379
     * We don't have to do this, but it keeps the compiler from
380
     * complaining about "now" possibly being used without being
381
     * set, even though it will never actually happen.
382
     */
383
0
    isc_time_settoepoch(&now);
384
0
  }
385
386
0
  LOCK(&manager->lock);
387
0
  LOCK(&timer->lock);
388
389
0
  if (purge) {
390
0
    (void)isc_task_purgerange(timer->task, timer,
391
0
            ISC_TIMEREVENT_FIRSTEVENT,
392
0
            ISC_TIMEREVENT_LASTEVENT, NULL);
393
0
  }
394
0
  timer->type = type;
395
0
  timer->expires = *expires;
396
0
  timer->interval = *interval;
397
0
  if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
398
0
    result = isc_time_add(&now, interval, &timer->idle);
399
0
  } else {
400
0
    isc_time_settoepoch(&timer->idle);
401
0
    result = ISC_R_SUCCESS;
402
0
  }
403
404
0
  if (result == ISC_R_SUCCESS) {
405
0
    if (type == isc_timertype_inactive) {
406
0
      deschedule(timer);
407
0
      result = ISC_R_SUCCESS;
408
0
    } else {
409
0
      result = schedule(timer, &now, true);
410
0
    }
411
0
  }
412
413
0
  UNLOCK(&timer->lock);
414
0
  UNLOCK(&manager->lock);
415
416
0
  return (result);
417
0
}
418
419
isc_timertype_t
420
0
isc_timer_gettype(isc_timer_t *timer) {
421
0
  isc_timertype_t t;
422
423
0
  REQUIRE(VALID_TIMER(timer));
424
425
0
  LOCK(&timer->lock);
426
0
  t = timer->type;
427
0
  UNLOCK(&timer->lock);
428
429
0
  return (t);
430
0
}
431
432
isc_result_t
433
0
isc_timer_touch(isc_timer_t *timer) {
434
0
  isc_result_t result;
435
0
  isc_time_t now;
436
437
  /*
438
   * Set the last-touched time of 'timer' to the current time.
439
   */
440
441
0
  REQUIRE(VALID_TIMER(timer));
442
443
0
  LOCK(&timer->lock);
444
445
  /*
446
   * We'd like to
447
   *
448
   *  REQUIRE(timer->type == isc_timertype_once);
449
   *
450
   * but we cannot without locking the manager lock too, which we
451
   * don't want to do.
452
   */
453
454
0
  TIME_NOW(&now);
455
0
  result = isc_time_add(&now, &timer->interval, &timer->idle);
456
457
0
  UNLOCK(&timer->lock);
458
459
0
  return (result);
460
0
}
461
462
void
463
0
isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
464
0
  REQUIRE(VALID_TIMER(timer));
465
0
  REQUIRE(timerp != NULL && *timerp == NULL);
466
0
  isc_refcount_increment(&timer->references);
467
468
0
  *timerp = timer;
469
0
}
470
471
void
472
0
isc_timer_detach(isc_timer_t **timerp) {
473
0
  isc_timer_t *timer;
474
475
  /*
476
   * Detach *timerp from its timer.
477
   */
478
479
0
  REQUIRE(timerp != NULL);
480
0
  timer = *timerp;
481
0
  REQUIRE(VALID_TIMER(timer));
482
483
0
  if (isc_refcount_decrement(&timer->references) == 1) {
484
0
    destroy(timer);
485
0
  }
486
487
0
  *timerp = NULL;
488
0
}
489
490
static void
491
0
dispatch(isc_timermgr_t *manager, isc_time_t *now) {
492
0
  bool done = false, post_event, need_schedule;
493
0
  isc_timerevent_t *event;
494
0
  isc_eventtype_t type = 0;
495
0
  isc_timer_t *timer;
496
0
  isc_result_t result;
497
0
  bool idle;
498
499
  /*!
500
   * The caller must be holding the manager lock.
501
   */
502
503
0
  while (manager->nscheduled > 0 && !done) {
504
0
    timer = isc_heap_element(manager->heap, 1);
505
0
    INSIST(timer != NULL && timer->type != isc_timertype_inactive);
506
0
    if (isc_time_compare(now, &timer->due) >= 0) {
507
0
      if (timer->type == isc_timertype_ticker) {
508
0
        type = ISC_TIMEREVENT_TICK;
509
0
        post_event = true;
510
0
        need_schedule = true;
511
0
      } else if (timer->type == isc_timertype_limited) {
512
0
        int cmp;
513
0
        cmp = isc_time_compare(now, &timer->expires);
514
0
        if (cmp >= 0) {
515
0
          type = ISC_TIMEREVENT_LIFE;
516
0
          post_event = true;
517
0
          need_schedule = false;
518
0
        } else {
519
0
          type = ISC_TIMEREVENT_TICK;
520
0
          post_event = true;
521
0
          need_schedule = true;
522
0
        }
523
0
      } else if (!isc_time_isepoch(&timer->expires) &&
524
0
           isc_time_compare(now, &timer->expires) >= 0)
525
0
      {
526
0
        type = ISC_TIMEREVENT_LIFE;
527
0
        post_event = true;
528
0
        need_schedule = false;
529
0
      } else {
530
0
        idle = false;
531
532
0
        LOCK(&timer->lock);
533
0
        if (!isc_time_isepoch(&timer->idle) &&
534
0
            isc_time_compare(now, &timer->idle) >= 0) {
535
0
          idle = true;
536
0
        }
537
0
        UNLOCK(&timer->lock);
538
0
        if (idle) {
539
0
          type = ISC_TIMEREVENT_IDLE;
540
0
          post_event = true;
541
0
          need_schedule = false;
542
0
        } else {
543
          /*
544
           * Idle timer has been touched;
545
           * reschedule.
546
           */
547
0
          XTRACEID("idle reschedule", timer);
548
0
          post_event = false;
549
0
          need_schedule = true;
550
0
        }
551
0
      }
552
553
0
      if (post_event) {
554
0
        XTRACEID("posting", timer);
555
        /*
556
         * XXX We could preallocate this event.
557
         */
558
0
        event = (isc_timerevent_t *)isc_event_allocate(
559
0
          manager->mctx, timer, type,
560
0
          timer->action, timer->arg,
561
0
          sizeof(*event));
562
563
0
        if (event != NULL) {
564
0
          event->due = timer->due;
565
0
          isc_task_send(timer->task,
566
0
                  ISC_EVENT_PTR(&event));
567
0
        } else {
568
0
          UNEXPECTED_ERROR(__FILE__, __LINE__,
569
0
               "%s",
570
0
               "couldn't allocate "
571
0
               "event");
572
0
        }
573
0
      }
574
575
0
      timer->index = 0;
576
0
      isc_heap_delete(manager->heap, 1);
577
0
      manager->nscheduled--;
578
579
0
      if (need_schedule) {
580
0
        result = schedule(timer, now, false);
581
0
        if (result != ISC_R_SUCCESS) {
582
0
          UNEXPECTED_ERROR(__FILE__, __LINE__,
583
0
               "%s: %u",
584
0
               "couldn't schedule "
585
0
               "timer",
586
0
               result);
587
0
        }
588
0
      }
589
0
    } else {
590
0
      manager->due = timer->due;
591
0
      done = true;
592
0
    }
593
0
  }
594
0
}
595
596
static isc_threadresult_t
597
0
run(void *uap) {
598
0
  isc_timermgr_t *manager = uap;
599
0
  isc_time_t now;
600
0
  isc_result_t result;
601
602
0
  LOCK(&manager->lock);
603
0
  while (!manager->done) {
604
0
    TIME_NOW(&now);
605
606
0
    XTRACETIME("running", now);
607
608
0
    dispatch(manager, &now);
609
610
0
    if (manager->nscheduled > 0) {
611
0
      XTRACETIME2("waituntil", manager->due, now);
612
0
      result = WAITUNTIL(&manager->wakeup, &manager->lock,
613
0
             &manager->due);
614
0
      INSIST(result == ISC_R_SUCCESS ||
615
0
             result == ISC_R_TIMEDOUT);
616
0
    } else {
617
0
      XTRACETIME("wait", now);
618
0
      WAIT(&manager->wakeup, &manager->lock);
619
0
    }
620
0
    XTRACE("wakeup");
621
0
  }
622
0
  UNLOCK(&manager->lock);
623
624
0
  return ((isc_threadresult_t)0);
625
0
}
626
627
static bool
628
0
sooner(void *v1, void *v2) {
629
0
  isc_timer_t *t1, *t2;
630
631
0
  t1 = v1;
632
0
  t2 = v2;
633
0
  REQUIRE(VALID_TIMER(t1));
634
0
  REQUIRE(VALID_TIMER(t2));
635
636
0
  if (isc_time_compare(&t1->due, &t2->due) < 0) {
637
0
    return (true);
638
0
  }
639
0
  return (false);
640
0
}
641
642
static void
643
0
set_index(void *what, unsigned int index) {
644
0
  isc_timer_t *timer;
645
646
0
  REQUIRE(VALID_TIMER(what));
647
0
  timer = what;
648
649
0
  timer->index = index;
650
0
}
651
652
isc_result_t
653
0
isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
654
0
  isc_timermgr_t *manager;
655
656
  /*
657
   * Create a timer manager.
658
   */
659
660
0
  REQUIRE(managerp != NULL && *managerp == NULL);
661
662
0
  manager = isc_mem_get(mctx, sizeof(*manager));
663
664
0
  manager->magic = TIMER_MANAGER_MAGIC;
665
0
  manager->mctx = NULL;
666
0
  manager->done = false;
667
0
  INIT_LIST(manager->timers);
668
0
  manager->nscheduled = 0;
669
0
  isc_time_settoepoch(&manager->due);
670
0
  manager->heap = NULL;
671
0
  isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
672
0
  isc_mutex_init(&manager->lock);
673
0
  isc_mem_attach(mctx, &manager->mctx);
674
0
  isc_condition_init(&manager->wakeup);
675
0
  isc_thread_create(run, manager, &manager->thread);
676
0
  isc_thread_setname(manager->thread, "isc-timer");
677
678
0
  *managerp = manager;
679
680
0
  return (ISC_R_SUCCESS);
681
0
}
682
683
void
684
0
isc_timermgr_poke(isc_timermgr_t *manager) {
685
0
  REQUIRE(VALID_MANAGER(manager));
686
687
0
  SIGNAL(&manager->wakeup);
688
0
}
689
690
void
691
0
isc__timermgr_destroy(isc_timermgr_t **managerp) {
692
0
  isc_timermgr_t *manager;
693
694
  /*
695
   * Destroy a timer manager.
696
   */
697
698
0
  REQUIRE(managerp != NULL);
699
0
  manager = *managerp;
700
0
  REQUIRE(VALID_MANAGER(manager));
701
702
0
  LOCK(&manager->lock);
703
704
0
  REQUIRE(EMPTY(manager->timers));
705
0
  manager->done = true;
706
707
0
  XTRACE("signal (destroy)");
708
0
  SIGNAL(&manager->wakeup);
709
710
0
  UNLOCK(&manager->lock);
711
712
  /*
713
   * Wait for thread to exit.
714
   */
715
0
  isc_thread_join(manager->thread, NULL);
716
717
  /*
718
   * Clean up.
719
   */
720
0
  (void)isc_condition_destroy(&manager->wakeup);
721
0
  isc_mutex_destroy(&manager->lock);
722
0
  isc_heap_destroy(&manager->heap);
723
0
  manager->magic = 0;
724
0
  isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
725
726
0
  *managerp = NULL;
727
0
}