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