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