Home | History | Annotate | Line # | Download | only in isc
loop.c revision 1.1.1.1
      1 /*	$NetBSD: loop.c,v 1.1.1.1 2025/01/26 16:12:31 christos 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 	loop->shuttingdown = true;
    146 
    147 	isc_loop_detach(&loop);
    148 }
    149 
    150 static void
    151 destroy_cb(uv_async_t *handle) {
    152 	isc_loop_t *loop = uv_handle_get_data(handle);
    153 
    154 	/* Again, the first close callback here is called last */
    155 	uv_close(&loop->async_trigger, isc__async_close);
    156 	uv_close(&loop->run_trigger, isc__job_close);
    157 	uv_close(&loop->destroy_trigger, NULL);
    158 	uv_close(&loop->pause_trigger, NULL);
    159 	uv_close(&loop->quiescent, NULL);
    160 
    161 	uv_walk(&loop->loop, loop_walk_cb, (char *)"destroy_cb");
    162 }
    163 
    164 static void
    165 shutdown_cb(uv_async_t *handle) {
    166 	isc_loop_t *loop = uv_handle_get_data(handle);
    167 	isc_loopmgr_t *loopmgr = loop->loopmgr;
    168 
    169 	/* Make sure, we can't be called again */
    170 	uv_close(&loop->shutdown_trigger, shutdown_trigger_close_cb);
    171 
    172 	if (DEFAULT_LOOP(loopmgr) == CURRENT_LOOP(loopmgr)) {
    173 		/* Stop the signal handlers */
    174 		isc_signal_stop(loopmgr->sigterm);
    175 		isc_signal_stop(loopmgr->sigint);
    176 
    177 		/* Free the signal handlers */
    178 		isc_signal_destroy(&loopmgr->sigterm);
    179 		isc_signal_destroy(&loopmgr->sigint);
    180 	}
    181 
    182 	enum cds_wfcq_ret ret = __cds_wfcq_splice_blocking(
    183 		&loop->async_jobs.head, &loop->async_jobs.tail,
    184 		&loop->teardown_jobs.head, &loop->teardown_jobs.tail);
    185 	INSIST(ret != CDS_WFCQ_RET_WOULDBLOCK);
    186 	int r = uv_async_send(&loop->async_trigger);
    187 	UV_RUNTIME_CHECK(uv_async_send, r);
    188 }
    189 
    190 static void
    191 loop_init(isc_loop_t *loop, isc_loopmgr_t *loopmgr, uint32_t tid,
    192 	  const char *kind) {
    193 	*loop = (isc_loop_t){
    194 		.tid = tid,
    195 		.loopmgr = loopmgr,
    196 		.run_jobs = ISC_LIST_INITIALIZER,
    197 	};
    198 
    199 	__cds_wfcq_init(&loop->async_jobs.head, &loop->async_jobs.tail);
    200 	__cds_wfcq_init(&loop->setup_jobs.head, &loop->setup_jobs.tail);
    201 	__cds_wfcq_init(&loop->teardown_jobs.head, &loop->teardown_jobs.tail);
    202 
    203 	int r = uv_loop_init(&loop->loop);
    204 	UV_RUNTIME_CHECK(uv_loop_init, r);
    205 
    206 	r = uv_async_init(&loop->loop, &loop->pause_trigger, pauseresume_cb);
    207 	UV_RUNTIME_CHECK(uv_async_init, r);
    208 	uv_handle_set_data(&loop->pause_trigger, loop);
    209 
    210 	r = uv_async_init(&loop->loop, &loop->shutdown_trigger, shutdown_cb);
    211 	UV_RUNTIME_CHECK(uv_async_init, r);
    212 	uv_handle_set_data(&loop->shutdown_trigger, loop);
    213 
    214 	r = uv_async_init(&loop->loop, &loop->async_trigger, isc__async_cb);
    215 	UV_RUNTIME_CHECK(uv_async_init, r);
    216 	uv_handle_set_data(&loop->async_trigger, loop);
    217 
    218 	r = uv_idle_init(&loop->loop, &loop->run_trigger);
    219 	UV_RUNTIME_CHECK(uv_idle_init, r);
    220 	uv_handle_set_data(&loop->run_trigger, loop);
    221 
    222 	r = uv_async_init(&loop->loop, &loop->destroy_trigger, destroy_cb);
    223 	UV_RUNTIME_CHECK(uv_async_init, r);
    224 	uv_handle_set_data(&loop->destroy_trigger, loop);
    225 
    226 	r = uv_prepare_init(&loop->loop, &loop->quiescent);
    227 	UV_RUNTIME_CHECK(uv_prepare_init, r);
    228 	uv_handle_set_data(&loop->quiescent, loop);
    229 
    230 	char name[16];
    231 	snprintf(name, sizeof(name), "%s-%08" PRIx32, kind, tid);
    232 	isc_mem_create(&loop->mctx);
    233 	isc_mem_setname(loop->mctx, name);
    234 
    235 	isc_refcount_init(&loop->references, 1);
    236 
    237 	loop->magic = LOOP_MAGIC;
    238 }
    239 
    240 static void
    241 quiescent_cb(uv_prepare_t *handle) {
    242 	UNUSED(handle);
    243 
    244 #if defined(RCU_QSBR)
    245 	/* safe memory reclamation */
    246 	rcu_quiescent_state();
    247 
    248 	/* mark the thread offline when polling */
    249 	rcu_thread_offline();
    250 #else
    251 	INSIST(!rcu_read_ongoing());
    252 #endif
    253 }
    254 
    255 static void
    256 helper_close(isc_loop_t *loop) {
    257 	int r = uv_loop_close(&loop->loop);
    258 	UV_RUNTIME_CHECK(uv_loop_close, r);
    259 
    260 	INSIST(cds_wfcq_empty(&loop->async_jobs.head, &loop->async_jobs.tail));
    261 
    262 	isc_mem_detach(&loop->mctx);
    263 }
    264 
    265 static void
    266 loop_close(isc_loop_t *loop) {
    267 	int r = uv_loop_close(&loop->loop);
    268 	UV_RUNTIME_CHECK(uv_loop_close, r);
    269 
    270 	INSIST(cds_wfcq_empty(&loop->async_jobs.head, &loop->async_jobs.tail));
    271 	INSIST(ISC_LIST_EMPTY(loop->run_jobs));
    272 
    273 	loop->magic = 0;
    274 
    275 	isc_mem_detach(&loop->mctx);
    276 }
    277 
    278 static void *
    279 helper_thread(void *arg) {
    280 	isc_loop_t *helper = (isc_loop_t *)arg;
    281 
    282 	int r = uv_prepare_start(&helper->quiescent, quiescent_cb);
    283 	UV_RUNTIME_CHECK(uv_prepare_start, r);
    284 
    285 	isc_barrier_wait(&helper->loopmgr->starting);
    286 
    287 	r = uv_run(&helper->loop, UV_RUN_DEFAULT);
    288 	UV_RUNTIME_CHECK(uv_run, r);
    289 
    290 	/* Invalidate the helper early */
    291 	helper->magic = 0;
    292 
    293 	isc_barrier_wait(&helper->loopmgr->stopping);
    294 
    295 	return NULL;
    296 }
    297 
    298 static void *
    299 loop_thread(void *arg) {
    300 	isc_loop_t *loop = (isc_loop_t *)arg;
    301 	isc_loopmgr_t *loopmgr = loop->loopmgr;
    302 	isc_loop_t *helper = &loopmgr->helpers[loop->tid];
    303 	char name[32];
    304 	/* Initialize the thread_local variables*/
    305 
    306 	REQUIRE(isc__loop_local == NULL || isc__loop_local == loop);
    307 	isc__loop_local = loop;
    308 
    309 	isc__tid_init(loop->tid);
    310 
    311 	/* Start the helper thread */
    312 	isc_thread_create(helper_thread, helper, &helper->thread);
    313 	snprintf(name, sizeof(name), "isc-helper-%04" PRIu32, loop->tid);
    314 	isc_thread_setname(helper->thread, name);
    315 
    316 	int r = uv_prepare_start(&loop->quiescent, quiescent_cb);
    317 	UV_RUNTIME_CHECK(uv_prepare_start, r);
    318 
    319 	isc_barrier_wait(&loopmgr->starting);
    320 
    321 	enum cds_wfcq_ret ret = __cds_wfcq_splice_blocking(
    322 		&loop->async_jobs.head, &loop->async_jobs.tail,
    323 		&loop->setup_jobs.head, &loop->setup_jobs.tail);
    324 	INSIST(ret != CDS_WFCQ_RET_WOULDBLOCK);
    325 
    326 	r = uv_async_send(&loop->async_trigger);
    327 	UV_RUNTIME_CHECK(uv_async_send, r);
    328 
    329 	r = uv_run(&loop->loop, UV_RUN_DEFAULT);
    330 	UV_RUNTIME_CHECK(uv_run, r);
    331 
    332 	isc__loop_local = NULL;
    333 
    334 	/* Invalidate the loop early */
    335 	loop->magic = 0;
    336 
    337 	/* Shutdown the helper thread */
    338 	r = uv_async_send(&helper->shutdown_trigger);
    339 	UV_RUNTIME_CHECK(uv_async_send, r);
    340 
    341 	isc_barrier_wait(&loopmgr->stopping);
    342 
    343 	return NULL;
    344 }
    345 
    346 /**
    347  * Public
    348  */
    349 
    350 static void
    351 threadpool_initialize(uint32_t workers) {
    352 	char buf[11];
    353 	int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
    354 			     &(size_t){ sizeof(buf) });
    355 	if (r == UV_ENOENT) {
    356 		snprintf(buf, sizeof(buf), "%" PRIu32, workers);
    357 		uv_os_setenv("UV_THREADPOOL_SIZE", buf);
    358 	}
    359 }
    360 
    361 static void
    362 loop_destroy(isc_loop_t *loop) {
    363 	int r = uv_async_send(&loop->destroy_trigger);
    364 	UV_RUNTIME_CHECK(uv_async_send, r);
    365 }
    366 
    367 #if ISC_LOOP_TRACE
    368 ISC_REFCOUNT_TRACE_IMPL(isc_loop, loop_destroy)
    369 #else
    370 ISC_REFCOUNT_IMPL(isc_loop, loop_destroy);
    371 #endif
    372 
    373 void
    374 isc_loopmgr_create(isc_mem_t *mctx, uint32_t nloops, isc_loopmgr_t **loopmgrp) {
    375 	isc_loopmgr_t *loopmgr = NULL;
    376 
    377 	REQUIRE(loopmgrp != NULL && *loopmgrp == NULL);
    378 	REQUIRE(nloops > 0);
    379 
    380 	threadpool_initialize(nloops);
    381 	isc__tid_initcount(nloops);
    382 
    383 	loopmgr = isc_mem_get(mctx, sizeof(*loopmgr));
    384 	*loopmgr = (isc_loopmgr_t){
    385 		.nloops = nloops,
    386 	};
    387 
    388 	isc_mem_attach(mctx, &loopmgr->mctx);
    389 
    390 	/* We need to double the number for loops and helpers */
    391 	isc_barrier_init(&loopmgr->pausing, loopmgr->nloops * 2);
    392 	isc_barrier_init(&loopmgr->resuming, loopmgr->nloops * 2);
    393 	isc_barrier_init(&loopmgr->starting, loopmgr->nloops * 2);
    394 	isc_barrier_init(&loopmgr->stopping, loopmgr->nloops * 2);
    395 
    396 	loopmgr->loops = isc_mem_cget(loopmgr->mctx, loopmgr->nloops,
    397 				      sizeof(loopmgr->loops[0]));
    398 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    399 		isc_loop_t *loop = &loopmgr->loops[i];
    400 		loop_init(loop, loopmgr, i, "loop");
    401 	}
    402 
    403 	loopmgr->helpers = isc_mem_cget(loopmgr->mctx, loopmgr->nloops,
    404 					sizeof(loopmgr->helpers[0]));
    405 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    406 		isc_loop_t *loop = &loopmgr->helpers[i];
    407 		loop_init(loop, loopmgr, i, "helper");
    408 	}
    409 
    410 	loopmgr->sigint = isc_signal_new(loopmgr, isc__loopmgr_signal, loopmgr,
    411 					 SIGINT);
    412 	loopmgr->sigterm = isc_signal_new(loopmgr, isc__loopmgr_signal, loopmgr,
    413 					  SIGTERM);
    414 
    415 	isc_signal_start(loopmgr->sigint);
    416 	isc_signal_start(loopmgr->sigterm);
    417 
    418 	loopmgr->magic = LOOPMGR_MAGIC;
    419 
    420 	*loopmgrp = loopmgr;
    421 }
    422 
    423 isc_job_t *
    424 isc_loop_setup(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
    425 	REQUIRE(VALID_LOOP(loop));
    426 	REQUIRE(cb != NULL);
    427 
    428 	isc_loopmgr_t *loopmgr = loop->loopmgr;
    429 	isc_job_t *job = isc_mem_get(loop->mctx, sizeof(*job));
    430 	*job = (isc_job_t){
    431 		.cb = cb,
    432 		.cbarg = cbarg,
    433 	};
    434 
    435 	cds_wfcq_node_init(&job->wfcq_node);
    436 
    437 	REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
    438 		atomic_load(&loopmgr->paused));
    439 
    440 	cds_wfcq_enqueue(&loop->setup_jobs.head, &loop->setup_jobs.tail,
    441 			 &job->wfcq_node);
    442 
    443 	return job;
    444 }
    445 
    446 isc_job_t *
    447 isc_loop_teardown(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
    448 	REQUIRE(VALID_LOOP(loop));
    449 
    450 	isc_loopmgr_t *loopmgr = loop->loopmgr;
    451 	isc_job_t *job = isc_mem_get(loop->mctx, sizeof(*job));
    452 	*job = (isc_job_t){
    453 		.cb = cb,
    454 		.cbarg = cbarg,
    455 	};
    456 	cds_wfcq_node_init(&job->wfcq_node);
    457 
    458 	REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
    459 		atomic_load(&loopmgr->paused));
    460 
    461 	cds_wfcq_enqueue(&loop->teardown_jobs.head, &loop->teardown_jobs.tail,
    462 			 &job->wfcq_node);
    463 
    464 	return job;
    465 }
    466 
    467 void
    468 isc_loopmgr_setup(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
    469 	REQUIRE(VALID_LOOPMGR(loopmgr));
    470 	REQUIRE(!atomic_load(&loopmgr->running) ||
    471 		atomic_load(&loopmgr->paused));
    472 
    473 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    474 		isc_loop_t *loop = &loopmgr->loops[i];
    475 		(void)isc_loop_setup(loop, cb, cbarg);
    476 	}
    477 }
    478 
    479 void
    480 isc_loopmgr_teardown(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
    481 	REQUIRE(VALID_LOOPMGR(loopmgr));
    482 	REQUIRE(!atomic_load(&loopmgr->running) ||
    483 		atomic_load(&loopmgr->paused));
    484 
    485 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    486 		isc_loop_t *loop = &loopmgr->loops[i];
    487 		(void)isc_loop_teardown(loop, cb, cbarg);
    488 	}
    489 }
    490 
    491 void
    492 isc_loopmgr_run(isc_loopmgr_t *loopmgr) {
    493 	REQUIRE(VALID_LOOPMGR(loopmgr));
    494 	RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->running,
    495 						     &(bool){ false }, true));
    496 
    497 	/*
    498 	 * Always ignore SIGPIPE.
    499 	 */
    500 	ignore_signal(SIGPIPE, SIG_IGN);
    501 
    502 	/*
    503 	 * The thread 0 is this one.
    504 	 */
    505 	for (size_t i = 1; i < loopmgr->nloops; i++) {
    506 		char name[32];
    507 		isc_loop_t *loop = &loopmgr->loops[i];
    508 
    509 		isc_thread_create(loop_thread, loop, &loop->thread);
    510 
    511 		snprintf(name, sizeof(name), "isc-loop-%04zu", i);
    512 		isc_thread_setname(loop->thread, name);
    513 	}
    514 
    515 	isc_thread_main(loop_thread, &loopmgr->loops[0]);
    516 }
    517 
    518 void
    519 isc_loopmgr_pause(isc_loopmgr_t *loopmgr) {
    520 	REQUIRE(VALID_LOOPMGR(loopmgr));
    521 	REQUIRE(isc_tid() != ISC_TID_UNKNOWN);
    522 
    523 	if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
    524 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    525 			      ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
    526 			      "loop exclusive mode: starting");
    527 	}
    528 
    529 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    530 		isc_loop_t *helper = &loopmgr->helpers[i];
    531 
    532 		int r = uv_async_send(&helper->pause_trigger);
    533 		UV_RUNTIME_CHECK(uv_async_send, r);
    534 	}
    535 
    536 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    537 		isc_loop_t *loop = &loopmgr->loops[i];
    538 
    539 		/* Skip current loop */
    540 		if (i == isc_tid()) {
    541 			continue;
    542 		}
    543 
    544 		int r = uv_async_send(&loop->pause_trigger);
    545 		UV_RUNTIME_CHECK(uv_async_send, r);
    546 	}
    547 
    548 	RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->paused,
    549 						     &(bool){ false }, true));
    550 	pause_loop(CURRENT_LOOP(loopmgr));
    551 
    552 	if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
    553 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    554 			      ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
    555 			      "loop exclusive mode: started");
    556 	}
    557 }
    558 
    559 void
    560 isc_loopmgr_resume(isc_loopmgr_t *loopmgr) {
    561 	REQUIRE(VALID_LOOPMGR(loopmgr));
    562 
    563 	if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
    564 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    565 			      ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
    566 			      "loop exclusive mode: ending");
    567 	}
    568 
    569 	RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->paused,
    570 						     &(bool){ true }, false));
    571 	resume_loop(CURRENT_LOOP(loopmgr));
    572 
    573 	if (isc_log_wouldlog(isc_lctx, ISC_LOG_DEBUG(1))) {
    574 		isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
    575 			      ISC_LOGMODULE_OTHER, ISC_LOG_DEBUG(1),
    576 			      "loop exclusive mode: ended");
    577 	}
    578 }
    579 
    580 void
    581 isc_loopmgr_destroy(isc_loopmgr_t **loopmgrp) {
    582 	isc_loopmgr_t *loopmgr = NULL;
    583 
    584 	REQUIRE(loopmgrp != NULL);
    585 	REQUIRE(VALID_LOOPMGR(*loopmgrp));
    586 
    587 	loopmgr = *loopmgrp;
    588 	*loopmgrp = NULL;
    589 
    590 	RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->running,
    591 						     &(bool){ true }, false));
    592 
    593 	/* Wait for all helpers to finish */
    594 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    595 		isc_loop_t *helper = &loopmgr->helpers[i];
    596 		isc_thread_join(helper->thread, NULL);
    597 	}
    598 
    599 	/* First wait for all loops to finish */
    600 	for (size_t i = 1; i < loopmgr->nloops; i++) {
    601 		isc_loop_t *loop = &loopmgr->loops[i];
    602 		isc_thread_join(loop->thread, NULL);
    603 	}
    604 
    605 	loopmgr->magic = 0;
    606 
    607 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    608 		isc_loop_t *helper = &loopmgr->helpers[i];
    609 		helper_close(helper);
    610 	}
    611 	isc_mem_cput(loopmgr->mctx, loopmgr->helpers, loopmgr->nloops,
    612 		     sizeof(loopmgr->helpers[0]));
    613 
    614 	for (size_t i = 0; i < loopmgr->nloops; i++) {
    615 		isc_loop_t *loop = &loopmgr->loops[i];
    616 		loop_close(loop);
    617 	}
    618 	isc_mem_cput(loopmgr->mctx, loopmgr->loops, loopmgr->nloops,
    619 		     sizeof(loopmgr->loops[0]));
    620 
    621 	isc_barrier_destroy(&loopmgr->starting);
    622 	isc_barrier_destroy(&loopmgr->stopping);
    623 	isc_barrier_destroy(&loopmgr->resuming);
    624 	isc_barrier_destroy(&loopmgr->pausing);
    625 
    626 	isc_mem_putanddetach(&loopmgr->mctx, loopmgr, sizeof(*loopmgr));
    627 }
    628 
    629 uint32_t
    630 isc_loopmgr_nloops(isc_loopmgr_t *loopmgr) {
    631 	REQUIRE(VALID_LOOPMGR(loopmgr));
    632 
    633 	return loopmgr->nloops;
    634 }
    635 
    636 isc_mem_t *
    637 isc_loop_getmctx(isc_loop_t *loop) {
    638 	REQUIRE(VALID_LOOP(loop));
    639 
    640 	return loop->mctx;
    641 }
    642 
    643 isc_loop_t *
    644 isc_loop_main(isc_loopmgr_t *loopmgr) {
    645 	REQUIRE(VALID_LOOPMGR(loopmgr));
    646 
    647 	return DEFAULT_LOOP(loopmgr);
    648 }
    649 
    650 isc_loop_t *
    651 isc_loop_get(isc_loopmgr_t *loopmgr, uint32_t tid) {
    652 	REQUIRE(VALID_LOOPMGR(loopmgr));
    653 	REQUIRE(tid < loopmgr->nloops);
    654 
    655 	return LOOP(loopmgr, tid);
    656 }
    657 
    658 void
    659 isc_loopmgr_blocking(isc_loopmgr_t *loopmgr) {
    660 	REQUIRE(VALID_LOOPMGR(loopmgr));
    661 
    662 	isc_signal_stop(loopmgr->sigterm);
    663 	isc_signal_stop(loopmgr->sigint);
    664 }
    665 
    666 void
    667 isc_loopmgr_nonblocking(isc_loopmgr_t *loopmgr) {
    668 	REQUIRE(VALID_LOOPMGR(loopmgr));
    669 
    670 	isc_signal_start(loopmgr->sigint);
    671 	isc_signal_start(loopmgr->sigterm);
    672 }
    673 
    674 isc_loopmgr_t *
    675 isc_loop_getloopmgr(isc_loop_t *loop) {
    676 	REQUIRE(VALID_LOOP(loop));
    677 
    678 	return loop->loopmgr;
    679 }
    680 
    681 isc_time_t
    682 isc_loop_now(isc_loop_t *loop) {
    683 	REQUIRE(VALID_LOOP(loop));
    684 
    685 	uint64_t msec = uv_now(&loop->loop);
    686 	isc_time_t t = {
    687 		.seconds = msec / MS_PER_SEC,
    688 		.nanoseconds = (msec % MS_PER_SEC) * NS_PER_MS,
    689 	};
    690 
    691 	return t;
    692 }
    693 
    694 bool
    695 isc_loop_shuttingdown(isc_loop_t *loop) {
    696 	REQUIRE(VALID_LOOP(loop));
    697 	REQUIRE(loop->tid == isc_tid());
    698 
    699 	return loop->shuttingdown;
    700 }
    701