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