xref: /netbsd-src/external/mpl/bind/dist/lib/isc/loop.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: loop.c,v 1.2 2025/01/26 16:25:37 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