Coverage Report

Created: 2025-08-26 06:58

/src/bind9/lib/isc/loop.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
#include <stdlib.h>
15
#include <sys/types.h>
16
#include <unistd.h>
17
18
#include <urcu/system.h>
19
20
#include <isc/async.h>
21
#include <isc/atomic.h>
22
#include <isc/barrier.h>
23
#include <isc/job.h>
24
#include <isc/list.h>
25
#include <isc/log.h>
26
#include <isc/loop.h>
27
#include <isc/magic.h>
28
#include <isc/mem.h>
29
#include <isc/mutex.h>
30
#include <isc/refcount.h>
31
#include <isc/result.h>
32
#include <isc/signal.h>
33
#include <isc/strerr.h>
34
#include <isc/thread.h>
35
#include <isc/tid.h>
36
#include <isc/time.h>
37
#include <isc/urcu.h>
38
#include <isc/util.h>
39
#include <isc/uv.h>
40
#include <isc/work.h>
41
42
#include "async_p.h"
43
#include "job_p.h"
44
#include "loop_p.h"
45
#include "thread_p.h"
46
47
/**
48
 * Private
49
 */
50
51
thread_local isc_loop_t *isc__loop_local = NULL;
52
isc_loopmgr_t *isc__loopmgr = NULL;
53
54
static void
55
0
ignore_signal(int sig, void (*handler)(int)) {
56
0
  struct sigaction sa = { .sa_handler = handler };
57
58
0
  if (sigfillset(&sa.sa_mask) != 0 || sigaction(sig, &sa, NULL) < 0) {
59
0
    FATAL_SYSERROR(errno, "ignore_signal(%d)", sig);
60
0
  }
61
0
}
62
63
void
64
0
isc_loopmgr_shutdown(void) {
65
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
66
0
  if (!atomic_compare_exchange_strong(&loopmgr->shuttingdown,
67
0
              &(bool){ false }, true))
68
0
  {
69
0
    return;
70
0
  }
71
72
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
73
0
    isc_loop_t *loop = &loopmgr->loops[i];
74
0
    int r;
75
76
0
    r = uv_async_send(&loop->shutdown_trigger);
77
0
    UV_RUNTIME_CHECK(uv_async_send, r);
78
0
  }
79
0
}
80
81
static void
82
0
isc__loopmgr_signal(void *arg ISC_ATTR_UNUSED, int signum) {
83
0
  switch (signum) {
84
0
  case SIGINT:
85
0
  case SIGTERM:
86
0
    isc_loopmgr_shutdown();
87
0
    break;
88
0
  default:
89
0
    UNREACHABLE();
90
0
  }
91
0
}
92
93
static void
94
0
pause_loop(isc_loop_t *loop) {
95
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
96
97
0
  rcu_thread_offline();
98
99
0
  loop->paused = true;
100
0
  (void)isc_barrier_wait(&loopmgr->pausing);
101
0
}
102
103
static void
104
0
resume_loop(isc_loop_t *loop) {
105
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
106
107
0
  (void)isc_barrier_wait(&loopmgr->resuming);
108
0
  loop->paused = false;
109
110
0
  rcu_thread_online();
111
0
}
112
113
static void
114
0
pauseresume_cb(uv_async_t *handle) {
115
0
  isc_loop_t *loop = uv_handle_get_data(handle);
116
117
0
  pause_loop(loop);
118
0
  resume_loop(loop);
119
0
}
120
121
#define XX(uc, lc)                                                         \
122
0
  case UV_##uc:                                                      \
123
0
    fprintf(stderr, "%s, %s: dangling %p: %p.type = %s\n",     \
124
0
      __func__, (char *)arg, handle->loop, handle, #lc); \
125
0
    break;
126
127
static void
128
0
loop_walk_cb(uv_handle_t *handle, void *arg) {
129
0
  if (uv_is_closing(handle)) {
130
0
    return;
131
0
  }
132
133
0
  switch (handle->type) {
134
0
    UV_HANDLE_TYPE_MAP(XX)
135
0
  default:
136
0
    fprintf(stderr, "%s, %s: dangling %p: %p.type = %s\n", __func__,
137
0
      (char *)arg, &handle->loop, handle, "unknown");
138
0
  }
139
0
}
140
141
static void
142
0
shutdown_trigger_close_cb(uv_handle_t *handle) {
143
0
  isc_loop_t *loop = uv_handle_get_data(handle);
144
145
0
  isc_loop_detach(&loop);
146
0
}
147
148
static void
149
0
destroy_cb(uv_async_t *handle) {
150
0
  isc_loop_t *loop = uv_handle_get_data(handle);
151
152
  /* Again, the first close callback here is called last */
153
0
  uv_close(&loop->async_trigger, isc__async_close);
154
0
  uv_close(&loop->run_trigger, isc__job_close);
155
0
  uv_close(&loop->destroy_trigger, NULL);
156
0
  uv_close(&loop->pause_trigger, NULL);
157
0
  uv_close(&loop->quiescent, NULL);
158
159
0
  uv_walk(&loop->loop, loop_walk_cb, (char *)"destroy_cb");
160
0
}
161
162
static void
163
0
shutdown_cb(uv_async_t *handle) {
164
0
  isc_loop_t *loop = uv_handle_get_data(handle);
165
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
166
167
  /* Make sure, we can't be called again */
168
0
  uv_close(&loop->shutdown_trigger, shutdown_trigger_close_cb);
169
170
  /* Mark this loop as shutting down */
171
0
  loop->shuttingdown = true;
172
173
0
  if (DEFAULT_LOOP(loopmgr) == CURRENT_LOOP(loopmgr)) {
174
    /* Stop the signal handlers */
175
0
    isc_signal_stop(loopmgr->sigterm);
176
0
    isc_signal_stop(loopmgr->sigint);
177
178
    /* Free the signal handlers */
179
0
    isc_signal_destroy(&loopmgr->sigterm);
180
0
    isc_signal_destroy(&loopmgr->sigint);
181
0
  }
182
183
0
  enum cds_wfcq_ret ret = __cds_wfcq_splice_blocking(
184
0
    &loop->async_jobs.head, &loop->async_jobs.tail,
185
0
    &loop->teardown_jobs.head, &loop->teardown_jobs.tail);
186
0
  INSIST(ret != CDS_WFCQ_RET_WOULDBLOCK);
187
0
  int r = uv_async_send(&loop->async_trigger);
188
0
  UV_RUNTIME_CHECK(uv_async_send, r);
189
0
}
190
191
static void
192
0
loop_init(isc_loop_t *loop, isc_tid_t tid, const char *kind) {
193
0
  *loop = (isc_loop_t){
194
0
    .tid = tid,
195
0
    .run_jobs = ISC_LIST_INITIALIZER,
196
0
  };
197
198
0
  __cds_wfcq_init(&loop->async_jobs.head, &loop->async_jobs.tail);
199
0
  __cds_wfcq_init(&loop->setup_jobs.head, &loop->setup_jobs.tail);
200
0
  __cds_wfcq_init(&loop->teardown_jobs.head, &loop->teardown_jobs.tail);
201
202
0
  int r = uv_loop_init(&loop->loop);
203
0
  UV_RUNTIME_CHECK(uv_loop_init, r);
204
205
0
  r = uv_async_init(&loop->loop, &loop->pause_trigger, pauseresume_cb);
206
0
  UV_RUNTIME_CHECK(uv_async_init, r);
207
0
  uv_handle_set_data(&loop->pause_trigger, loop);
208
209
0
  r = uv_async_init(&loop->loop, &loop->shutdown_trigger, shutdown_cb);
210
0
  UV_RUNTIME_CHECK(uv_async_init, r);
211
0
  uv_handle_set_data(&loop->shutdown_trigger, loop);
212
213
0
  r = uv_async_init(&loop->loop, &loop->async_trigger, isc__async_cb);
214
0
  UV_RUNTIME_CHECK(uv_async_init, r);
215
0
  uv_handle_set_data(&loop->async_trigger, loop);
216
217
0
  r = uv_idle_init(&loop->loop, &loop->run_trigger);
218
0
  UV_RUNTIME_CHECK(uv_idle_init, r);
219
0
  uv_handle_set_data(&loop->run_trigger, loop);
220
221
0
  r = uv_async_init(&loop->loop, &loop->destroy_trigger, destroy_cb);
222
0
  UV_RUNTIME_CHECK(uv_async_init, r);
223
0
  uv_handle_set_data(&loop->destroy_trigger, loop);
224
225
0
  r = uv_prepare_init(&loop->loop, &loop->quiescent);
226
0
  UV_RUNTIME_CHECK(uv_prepare_init, r);
227
0
  uv_handle_set_data(&loop->quiescent, loop);
228
229
0
  isc_mem_create(kind, &loop->mctx);
230
231
0
  isc_refcount_init(&loop->references, 1);
232
233
0
  loop->magic = LOOP_MAGIC;
234
0
}
235
236
static void
237
0
quiescent_cb(uv_prepare_t *handle) {
238
0
  UNUSED(handle);
239
240
#if defined(RCU_QSBR)
241
  /* safe memory reclamation */
242
  rcu_quiescent_state();
243
244
  /* mark the thread offline when polling */
245
  rcu_thread_offline();
246
#else
247
0
  INSIST(!rcu_read_ongoing());
248
0
#endif
249
0
}
250
251
static void
252
0
helper_close(isc_loop_t *loop) {
253
0
  int r = uv_loop_close(&loop->loop);
254
0
  UV_RUNTIME_CHECK(uv_loop_close, r);
255
256
0
  INSIST(cds_wfcq_empty(&loop->async_jobs.head, &loop->async_jobs.tail));
257
258
0
  isc_mem_detach(&loop->mctx);
259
0
}
260
261
static void
262
0
loop_close(isc_loop_t *loop) {
263
0
  int r = uv_loop_close(&loop->loop);
264
0
  UV_RUNTIME_CHECK(uv_loop_close, r);
265
266
0
  INSIST(cds_wfcq_empty(&loop->async_jobs.head, &loop->async_jobs.tail));
267
0
  INSIST(ISC_LIST_EMPTY(loop->run_jobs));
268
269
0
  loop->magic = 0;
270
271
0
  isc_mem_detach(&loop->mctx);
272
0
}
273
274
static void *
275
0
helper_thread(void *arg) {
276
0
  isc_loop_t *helper = (isc_loop_t *)arg;
277
278
0
  int r = uv_prepare_start(&helper->quiescent, quiescent_cb);
279
0
  UV_RUNTIME_CHECK(uv_prepare_start, r);
280
281
0
  isc_barrier_wait(&isc__loopmgr->starting);
282
283
0
  r = uv_run(&helper->loop, UV_RUN_DEFAULT);
284
0
  UV_RUNTIME_CHECK(uv_run, r);
285
286
  /* Invalidate the helper early */
287
0
  helper->magic = 0;
288
289
0
  isc_barrier_wait(&isc__loopmgr->stopping);
290
291
0
  return NULL;
292
0
}
293
294
static void *
295
0
loop_thread(void *arg) {
296
0
  isc_loop_t *loop = (isc_loop_t *)arg;
297
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
298
0
  isc_loop_t *helper = &loopmgr->helpers[loop->tid];
299
0
  char name[32];
300
  /* Initialize the thread_local variables*/
301
302
0
  REQUIRE(isc__loop_local == NULL || isc__loop_local == loop);
303
0
  isc__loop_local = loop;
304
305
0
  isc__tid_init(loop->tid);
306
307
  /* Start the helper thread */
308
0
  isc_thread_create(helper_thread, helper, &helper->thread);
309
0
  snprintf(name, sizeof(name), "isc-helper-%04" PRItid, loop->tid);
310
0
  isc_thread_setname(helper->thread, name);
311
312
0
  int r = uv_prepare_start(&loop->quiescent, quiescent_cb);
313
0
  UV_RUNTIME_CHECK(uv_prepare_start, r);
314
315
0
  isc_barrier_wait(&loopmgr->starting);
316
317
0
  enum cds_wfcq_ret ret = __cds_wfcq_splice_blocking(
318
0
    &loop->async_jobs.head, &loop->async_jobs.tail,
319
0
    &loop->setup_jobs.head, &loop->setup_jobs.tail);
320
0
  INSIST(ret != CDS_WFCQ_RET_WOULDBLOCK);
321
322
0
  r = uv_async_send(&loop->async_trigger);
323
0
  UV_RUNTIME_CHECK(uv_async_send, r);
324
325
0
  r = uv_run(&loop->loop, UV_RUN_DEFAULT);
326
0
  UV_RUNTIME_CHECK(uv_run, r);
327
328
0
  isc__loop_local = NULL;
329
330
  /* Invalidate the loop early */
331
0
  loop->magic = 0;
332
333
  /* Shutdown the helper thread */
334
0
  r = uv_async_send(&helper->shutdown_trigger);
335
0
  UV_RUNTIME_CHECK(uv_async_send, r);
336
337
0
  isc_barrier_wait(&loopmgr->stopping);
338
339
0
  return NULL;
340
0
}
341
342
/**
343
 * Public
344
 */
345
346
static void
347
0
threadpool_initialize(uint32_t workers) {
348
0
  char buf[11];
349
0
  int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
350
0
           &(size_t){ sizeof(buf) });
351
0
  if (r == UV_ENOENT) {
352
0
    snprintf(buf, sizeof(buf), "%" PRIu32, workers);
353
0
    uv_os_setenv("UV_THREADPOOL_SIZE", buf);
354
0
  }
355
0
}
356
357
static void
358
0
loop_destroy(isc_loop_t *loop) {
359
0
  int r = uv_async_send(&loop->destroy_trigger);
360
0
  UV_RUNTIME_CHECK(uv_async_send, r);
361
0
}
362
363
#if ISC_LOOP_TRACE
364
ISC_REFCOUNT_TRACE_IMPL(isc_loop, loop_destroy)
365
#else
366
ISC_REFCOUNT_IMPL(isc_loop, loop_destroy);
367
#endif
368
369
void
370
0
isc_loopmgr_create(isc_mem_t *mctx, uint32_t nloops) {
371
0
  REQUIRE(isc__loopmgr == NULL);
372
0
  REQUIRE(nloops > 0);
373
374
0
  isc_loopmgr_t *loopmgr = NULL;
375
376
0
  threadpool_initialize(nloops);
377
0
  isc__tid_initcount(nloops);
378
379
0
  loopmgr = isc_mem_get(mctx, sizeof(*loopmgr));
380
0
  *loopmgr = (isc_loopmgr_t){
381
0
    .nloops = nloops,
382
0
    .magic = LOOPMGR_MAGIC,
383
0
  };
384
385
0
  isc_mem_attach(mctx, &loopmgr->mctx);
386
387
  /* We need to double the number for loops and helpers */
388
0
  isc_barrier_init(&loopmgr->pausing, loopmgr->nloops * 2);
389
0
  isc_barrier_init(&loopmgr->resuming, loopmgr->nloops * 2);
390
0
  isc_barrier_init(&loopmgr->starting, loopmgr->nloops * 2);
391
0
  isc_barrier_init(&loopmgr->stopping, loopmgr->nloops * 2);
392
393
0
  loopmgr->loops = isc_mem_cget(loopmgr->mctx, loopmgr->nloops,
394
0
              sizeof(loopmgr->loops[0]));
395
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
396
0
    isc_loop_t *loop = &loopmgr->loops[i];
397
0
    loop_init(loop, i, "loop");
398
0
  }
399
400
0
  loopmgr->helpers = isc_mem_cget(loopmgr->mctx, loopmgr->nloops,
401
0
          sizeof(loopmgr->helpers[0]));
402
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
403
0
    isc_loop_t *loop = &loopmgr->helpers[i];
404
0
    loop_init(loop, i, "helper");
405
0
  }
406
407
0
  isc__loopmgr = loopmgr;
408
409
0
  loopmgr->sigint = isc_signal_new(isc__loopmgr_signal, loopmgr, SIGINT);
410
0
  loopmgr->sigterm = isc_signal_new(isc__loopmgr_signal, loopmgr,
411
0
            SIGTERM);
412
413
0
  isc_signal_start(loopmgr->sigint);
414
0
  isc_signal_start(loopmgr->sigterm);
415
0
}
416
417
isc_job_t *
418
0
isc_loop_setup(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
419
0
  REQUIRE(VALID_LOOP(loop));
420
0
  REQUIRE(cb != NULL);
421
422
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
423
0
  isc_job_t *job = isc_mem_get(loop->mctx, sizeof(*job));
424
0
  *job = (isc_job_t){
425
0
    .cb = cb,
426
0
    .cbarg = cbarg,
427
0
  };
428
429
0
  cds_wfcq_node_init(&job->wfcq_node);
430
431
0
  REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
432
0
    atomic_load(&loopmgr->paused));
433
434
0
  cds_wfcq_enqueue(&loop->setup_jobs.head, &loop->setup_jobs.tail,
435
0
       &job->wfcq_node);
436
437
0
  return job;
438
0
}
439
440
isc_job_t *
441
0
isc_loop_teardown(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
442
0
  REQUIRE(VALID_LOOP(loop));
443
444
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
445
0
  isc_job_t *job = isc_mem_get(loop->mctx, sizeof(*job));
446
0
  *job = (isc_job_t){
447
0
    .cb = cb,
448
0
    .cbarg = cbarg,
449
0
  };
450
0
  cds_wfcq_node_init(&job->wfcq_node);
451
452
0
  REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
453
0
    atomic_load(&loopmgr->paused));
454
455
0
  cds_wfcq_enqueue(&loop->teardown_jobs.head, &loop->teardown_jobs.tail,
456
0
       &job->wfcq_node);
457
458
0
  return job;
459
0
}
460
461
void
462
0
isc_loopmgr_setup(isc_job_cb cb, void *cbarg) {
463
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
464
0
  REQUIRE(!atomic_load(&isc__loopmgr->running) ||
465
0
    atomic_load(&isc__loopmgr->paused));
466
467
0
  for (size_t i = 0; i < isc__loopmgr->nloops; i++) {
468
0
    isc_loop_t *loop = &isc__loopmgr->loops[i];
469
0
    (void)isc_loop_setup(loop, cb, cbarg);
470
0
  }
471
0
}
472
473
void
474
0
isc_loopmgr_teardown(isc_job_cb cb, void *cbarg) {
475
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
476
0
  REQUIRE(!atomic_load(&isc__loopmgr->running) ||
477
0
    atomic_load(&isc__loopmgr->paused));
478
479
0
  for (size_t i = 0; i < isc__loopmgr->nloops; i++) {
480
0
    isc_loop_t *loop = &isc__loopmgr->loops[i];
481
0
    (void)isc_loop_teardown(loop, cb, cbarg);
482
0
  }
483
0
}
484
485
void
486
0
isc_loopmgr_run(void) {
487
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
488
0
  RUNTIME_CHECK(atomic_compare_exchange_strong(&isc__loopmgr->running,
489
0
                 &(bool){ false }, true));
490
491
  /*
492
   * Always ignore SIGPIPE.
493
   */
494
0
  ignore_signal(SIGPIPE, SIG_IGN);
495
496
0
  isc__thread_initialize();
497
498
  /*
499
   * The thread 0 is this one.
500
   */
501
0
  for (size_t i = 1; i < isc__loopmgr->nloops; i++) {
502
0
    char name[32];
503
0
    isc_loop_t *loop = &isc__loopmgr->loops[i];
504
505
0
    isc_thread_create(loop_thread, loop, &loop->thread);
506
507
0
    snprintf(name, sizeof(name), "isc-loop-%04zu", i);
508
0
    isc_thread_setname(loop->thread, name);
509
0
  }
510
511
0
  isc_thread_main(loop_thread, &isc__loopmgr->loops[0]);
512
0
}
513
514
void
515
0
isc_loopmgr_pause(void) {
516
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
517
0
  REQUIRE(isc_tid() != ISC_TID_UNKNOWN);
518
519
0
  if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
520
0
    isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER,
521
0
            ISC_LOG_DEBUG(1),
522
0
            "loop exclusive mode: starting");
523
0
  }
524
525
0
  for (size_t i = 0; i < isc__loopmgr->nloops; i++) {
526
0
    isc_loop_t *helper = &isc__loopmgr->helpers[i];
527
528
0
    int r = uv_async_send(&helper->pause_trigger);
529
0
    UV_RUNTIME_CHECK(uv_async_send, r);
530
0
  }
531
532
0
  for (size_t i = 0; i < isc__loopmgr->nloops; i++) {
533
0
    isc_loop_t *loop = &isc__loopmgr->loops[i];
534
535
    /* Skip current loop */
536
0
    if (i == (size_t)isc_tid()) {
537
0
      continue;
538
0
    }
539
540
0
    int r = uv_async_send(&loop->pause_trigger);
541
0
    UV_RUNTIME_CHECK(uv_async_send, r);
542
0
  }
543
544
0
  RUNTIME_CHECK(atomic_compare_exchange_strong(&isc__loopmgr->paused,
545
0
                 &(bool){ false }, true));
546
0
  pause_loop(CURRENT_LOOP(isc__loopmgr));
547
548
0
  if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
549
0
    isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER,
550
0
            ISC_LOG_DEBUG(1), "loop exclusive mode: started");
551
0
  }
552
0
}
553
554
void
555
0
isc_loopmgr_resume(void) {
556
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
557
558
0
  if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
559
0
    isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER,
560
0
            ISC_LOG_DEBUG(1), "loop exclusive mode: ending");
561
0
  }
562
563
0
  RUNTIME_CHECK(atomic_compare_exchange_strong(&isc__loopmgr->paused,
564
0
                 &(bool){ true }, false));
565
0
  resume_loop(CURRENT_LOOP(isc__loopmgr));
566
567
0
  if (isc_log_wouldlog(ISC_LOG_DEBUG(1))) {
568
0
    isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_OTHER,
569
0
            ISC_LOG_DEBUG(1), "loop exclusive mode: ended");
570
0
  }
571
0
}
572
573
bool
574
0
isc_loopmgr_paused(void) {
575
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
576
577
0
  return atomic_load(&isc__loopmgr->paused);
578
0
}
579
580
void
581
0
isc_loopmgr_destroy(void) {
582
0
  isc_loopmgr_t *loopmgr = isc__loopmgr;
583
584
0
  RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->running,
585
0
                 &(bool){ true }, false));
586
587
0
  isc__loopmgr = NULL;
588
589
  /* Wait for all helpers to finish */
590
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
591
0
    isc_loop_t *helper = &loopmgr->helpers[i];
592
0
    isc_thread_join(helper->thread, NULL);
593
0
  }
594
595
  /* First wait for all loops to finish */
596
0
  for (size_t i = 1; i < loopmgr->nloops; i++) {
597
0
    isc_loop_t *loop = &loopmgr->loops[i];
598
0
    isc_thread_join(loop->thread, NULL);
599
0
  }
600
601
0
  loopmgr->magic = 0;
602
603
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
604
0
    isc_loop_t *helper = &loopmgr->helpers[i];
605
0
    helper_close(helper);
606
0
  }
607
0
  isc_mem_cput(loopmgr->mctx, loopmgr->helpers, loopmgr->nloops,
608
0
         sizeof(loopmgr->helpers[0]));
609
610
0
  for (size_t i = 0; i < loopmgr->nloops; i++) {
611
0
    isc_loop_t *loop = &loopmgr->loops[i];
612
0
    loop_close(loop);
613
0
  }
614
0
  isc_mem_cput(loopmgr->mctx, loopmgr->loops, loopmgr->nloops,
615
0
         sizeof(loopmgr->loops[0]));
616
617
0
  isc_barrier_destroy(&loopmgr->starting);
618
0
  isc_barrier_destroy(&loopmgr->stopping);
619
0
  isc_barrier_destroy(&loopmgr->resuming);
620
0
  isc_barrier_destroy(&loopmgr->pausing);
621
622
0
  isc_mem_putanddetach(&loopmgr->mctx, loopmgr, sizeof(*loopmgr));
623
624
0
  isc__thread_shutdown();
625
0
}
626
627
uint32_t
628
0
isc_loopmgr_nloops(void) {
629
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
630
631
0
  return isc__loopmgr->nloops;
632
0
}
633
634
isc_mem_t *
635
0
isc_loop_getmctx(isc_loop_t *loop) {
636
0
  REQUIRE(VALID_LOOP(loop));
637
638
0
  return loop->mctx;
639
0
}
640
641
isc_loop_t *
642
0
isc_loop_main(void) {
643
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
644
645
0
  return DEFAULT_LOOP(isc__loopmgr);
646
0
}
647
648
isc_loop_t *
649
0
isc_loop_get(isc_tid_t tid) {
650
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
651
0
  REQUIRE((uint32_t)tid < isc__loopmgr->nloops);
652
653
0
  return LOOP(isc__loopmgr, tid);
654
0
}
655
656
void
657
0
isc_loopmgr_blocking(void) {
658
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
659
660
0
  isc_signal_stop(isc__loopmgr->sigterm);
661
0
  isc_signal_stop(isc__loopmgr->sigint);
662
0
}
663
664
void
665
0
isc_loopmgr_nonblocking(void) {
666
0
  REQUIRE(VALID_LOOPMGR(isc__loopmgr));
667
668
0
  isc_signal_start(isc__loopmgr->sigint);
669
0
  isc_signal_start(isc__loopmgr->sigterm);
670
0
}
671
672
isc_time_t
673
0
isc_loop_now(isc_loop_t *loop) {
674
0
  REQUIRE(VALID_LOOP(loop));
675
676
0
  uint64_t msec = uv_now(&loop->loop);
677
0
  isc_time_t t = {
678
0
    .seconds = msec / MS_PER_SEC,
679
0
    .nanoseconds = (msec % MS_PER_SEC) * NS_PER_MS,
680
0
  };
681
682
0
  return t;
683
0
}
684
685
bool
686
0
isc_loop_shuttingdown(isc_loop_t *loop) {
687
0
  REQUIRE(VALID_LOOP(loop));
688
0
  REQUIRE(loop->tid == isc_tid());
689
690
0
  return loop->shuttingdown;
691
0
}
692
693
isc_loop_t *
694
0
isc_loop_helper(isc_loop_t *loop) {
695
0
  REQUIRE(VALID_LOOP(loop));
696
697
0
  return &isc__loopmgr->helpers[loop->tid];
698
0
}