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