--- /dev/null
+<!--
+Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+
+SPDX-License-Identifier: MPL-2.0
+
+This Source Code Form is subject to the terms of the Mozilla Public
+License, v. 2.0. If a copy of the MPL was not distributed with this
+file, you can obtain one at https://mozilla.org/MPL/2.0/.
+
+See the COPYRIGHT file distributed with this work for additional
+information regarding copyright ownership.
+-->
+
+# Loop Manager
+
+This document aims to describe the design of the basic event loop handling in
+the BIND 9.
+
+Every application is expected to create and use a single ``isc_loopmgr_t``
+instance, but the ``isc_loopmgr`` API itself doesn't enforce this requirement.
+
+## Event Loops
+
+The loop manager creates *N* event loops (of type ``isc_loop_t``), where *N* is
+specified by the caller when creating the loop manager. The number of event
+loops is usually same as the number of logical CPUs. The minimum *N* is 1, and
+maximum is limited only by the machine resources.
+
+For each event loop, a thread is created by the loop manager. The
+``isc_loop_t`` object itself is built on top of ``uv_loop_t``. ``uv_loop_t``
+is not thread-safe, and this is also true for ``isc_loop_t``. If you need to
+run an event on a different event loop, see below for the ``isc_async`` API.
+
+The application can get a reference to the current event loop using
+``isc_loop_current()``, to the main event loop (loop 0) that will always exist
+using ``isc_loop_main()`` and to an arbitrary event loop using
+``isc_loop_get()``.
+
+## Application Start and Stop
+
+Every application MUST add its initial events using ``isc_loopmgr_setup()`` to
+be run on all initialized event loops or ``isc_loop_setup()`` to be run on a
+selected event loop.
+
+Applications MAY also add events to be run when the application is shut down by
+calling ``isc_loopmgr_teardown()`` (or ``isc_loop_teardown()`` for a specific
+event loop).
+
+After the setup and teardown events have been configured, the application may
+be started via ``isc_loopmgr_run()``. ``isc_loopmgr_run()`` will block for the
+caller while event loops are running. When the work is done,
+``isc_loopmgr_shutdown()`` must be run from within one of the event loops; this
+will cause all loops to be shut down and ``isc_loopmgr_run()`` to return.
+
+The most notable change from the ``isc_app`` API is the lack of a blocked
+``main`` thread. The loop manager starts the **main** event loop on the
+**main** thread when the application is started.
+
+This API now replaces the old ``isc_app`` API.
+
+## Signal Handling
+
+The loop manager itself takes care of handling the ``SIGTERM`` and ``SIGINT``
+signals, but the application MAY add more handlers via ``isc_signal`` API. In
+``named``, for example, ``SIGHUP`` is used to trigger an application reload.
+
+## Event scheduling
+
+The application may add events to the event loop via ``isc_job_run()`` for jobs
+on the same event loop, or via ``isc_async_run()`` for jobs to be passed to
+other event loops. Both functions take the event loop, the callback and the
+callback argument as parameters.
+
+Generally ``isc_job_run()`` is more direct, as it schedules the event directly
+on the event loop and doesn't use locking, and should be preferred unless you
+need to run the event on a different thread.
+
+``isc_async_run()`` is the only new thread-safe function provided by the loop
+manager, uses locked list to collect new jobs and uv_async() primitive to
+enqueue the collected jobs onto the event loop.
+
+## Tasks
+
+The ``isc_task`` API has been modified to run the tasks directly on the loop
+manager. The new ``isc_job`` and ``isc_async`` APIs are preferred for simple
+events; the ``isc_task`` API is provided for backward-compatibility purposes
+and thus is also thread-safe because it uses locking and uv_async() to enqueue
+events onto the event loop.
+
+## Timers
+
+The ``isc_timer`` API is now built on top of the ``uv_timer_t`` object. It has
+been changed to support only ``ticker`` and ``once`` timers, and now uses
+``isc_timer_start()`` and ``isc_timer_stop()`` instead of changing the timer
+type to ``inactive``. The ``isc_timer_t`` object is not thread-safe.
+
+## Network Manager
+
+The network manager has been changed to use the loop manager event loops
+instead of managing its own event loops.
+
+The new network manager calls are not thread-safe; all connect/read/write
+functions MUST be called from the thread that created the network manager
+socket.
+
+The ``isc_nm_listen*()`` functions MUST be called from the ``main`` loop.
include/isc/app.h \
include/isc/assertions.h \
include/isc/astack.h \
+ include/isc/async.h \
include/isc/atomic.h \
include/isc/attributes.h \
include/isc/backtrace.h \
include/isc/httpd.h \
include/isc/interfaceiter.h \
include/isc/iterated_hash.h \
+ include/isc/job.h \
include/isc/lang.h \
include/isc/lex.h \
include/isc/list.h \
include/isc/log.h \
+ include/isc/loop.h \
include/isc/magic.h \
include/isc/managers.h \
include/isc/md.h \
include/isc/rwlock.h \
include/isc/safe.h \
include/isc/serial.h \
+ include/isc/signal.h\
include/isc/siphash.h \
include/isc/sockaddr.h \
include/isc/stat.h \
include/isc/syslog.h \
include/isc/task.h \
include/isc/thread.h \
+ include/isc/tid.h \
include/isc/time.h \
include/isc/timer.h \
include/isc/tls.h \
include/isc/url.h \
include/isc/utf8.h \
include/isc/util.h \
- include/isc/uv.h
+ include/isc/uv.h \
+ include/isc/work.h
libisc_la_SOURCES = \
$(libisc_la_HEADERS) \
app.c \
assertions.c \
astack.c \
+ async.c \
backtrace.c \
base32.c \
base64.c \
interfaceiter.c \
iterated_hash.c \
jemalloc_shim.h \
+ job.c \
+ job_p.h \
lex.c \
lib.c \
log.c \
+ loop.c \
+ loop_p.h \
managers.c \
md.c \
mem.c \
rwlock.c \
safe.c \
serial.c \
+ signal.c \
siphash.c \
sockaddr.c \
stats.c \
task.c \
task_p.h \
thread.c \
+ tid.c \
time.c \
timer.c \
timer_p.h \
trampoline_p.h \
url.c \
utf8.c \
- uv.c
+ uv.c \
+ work.c
libisc_la_CPPFLAGS = \
$(AM_CPPFLAGS) \
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/async.h>
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/condition.h>
+#include <isc/job.h>
+#include <isc/list.h>
+#include <isc/loop.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/signal.h>
+#include <isc/strerr.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+#include <isc/uv.h>
+#include <isc/work.h>
+
+#include "job_p.h"
+#include "loop_p.h"
+
+void
+isc_async_run(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
+ int r;
+ isc_job_t *job = NULL;
+
+ REQUIRE(VALID_LOOP(loop));
+ REQUIRE(cb != NULL);
+
+ job = isc__job_new(loop, cb, cbarg);
+
+ /* Now send the half initialized job to loop queue */
+ LOCK(&loop->queue_lock);
+ ISC_LIST_APPEND(loop->queue_jobs, job, link);
+ UNLOCK(&loop->queue_lock);
+
+ r = uv_async_send(&loop->queue_trigger);
+ UV_RUNTIME_CHECK(uv_async_send, r);
+}
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/async.h
+ * \brief The isc_async unit provides a way to schedule jobs on any isc
+ * event loop (isc_loop unit)
+ *
+ * The unit is built around the uv_async_t primitive and locked list with
+ * isc_job_cb. Jobs are first scheduled onto the locked list, then the
+ * uv_async_send() is called and the uv_async_t callback processes the enqueued
+ * jobs are scheduled to be run on the isc event loop.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/job.h>
+#include <isc/lang.h>
+#include <isc/loop.h>
+#include <isc/mem.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_async_run(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
+/*%<
+ * Schedule the job callback 'cb' to be run on the 'loop' event loop.
+ *
+ * Requires:
+ *
+ *\li 'loop' is a valid isc event loop
+ *\li 'cb' is a callback function, must be non-NULL
+ *\li 'cbarg' is passed to the 'cb' as the only argument, may be NULL
+ */
+
+ISC_LANG_ENDDECLS
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file isc/job.h
+ * \brief The isc_job unit provides a way to schedule jobs on the
+ * currently running isc event loop. This is distinct from isc_async,
+ * which can be used to send events to another, specified loop.
+ *
+ * The unit is built around the uv_idle_t primitive and it directly schedules
+ * the callback to be run on the isc event loop.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/types.h>
+
+typedef void (*isc_job_cb)(void *);
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_job_run(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg);
+/*%<
+ * Schedule the job callback 'cb' to be run on the currently
+ * running event loop.
+ *
+ * Requires:
+ *
+ *\li 'loopmgr' is the active loop manager.
+ *\li 'cb' is a callback function, must be non-NULL
+ *\li 'cbarg' is passed to the 'cb' as the only argument, may be NULL
+ */
+
+ISC_LANG_ENDDECLS
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/types.h>
+
+typedef void (*isc_job_cb)(void *);
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_loopmgr_create(isc_mem_t *mctx, uint32_t nloops, isc_loopmgr_t **loopmgrp);
+/*%<
+ * Create a loop manager supporting 'nloops' loops.
+ *
+ * Requires:
+ *\li 'nloops' is greater than 0.
+ */
+
+void
+isc_loopmgr_destroy(isc_loopmgr_t **loopmgrp);
+/*%<
+ * Destroy the loop manager pointed to by 'loopmgrp'.
+ *
+ * Requires:
+ *\li 'loopmgr' points to a valid loop manager.
+ */
+
+void
+isc_loopmgr_shutdown(isc_loopmgr_t *loopmgr);
+/*%<
+ * Request shutdown of the loop manager 'loopmgr'.
+ *
+ * This will stop all signal handlers and send shutdown events to
+ * all active loops. As a final action on shutting down, each loop
+ * will run the function (or functions) set by isc_loopmgr_teardown()
+ * or isc_loop_teardown().
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ */
+
+void
+isc_loopmgr_run(isc_loopmgr_t *loopmgr);
+/*%<
+ * Run the loops in 'loopmgr'. Each loop will start by running the
+ * function (or functions) set by isc_loopmgr_setup() or isc_loop_setup().
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ */
+
+void
+isc_loopmgr_pause(isc_loopmgr_t *loopmgr);
+/*%<
+ * Send pause events to all running loops in 'loopmgr' except the
+ * current one. This can only be called from a running loop.
+ * All the paused loops will wait until isc_loopmgr_resume() is
+ * run in the calling loop before continuing.
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ *\li We are in a running loop.
+ */
+
+void
+isc_loopmgr_resume(isc_loopmgr_t *loopmgr);
+/*%<
+ * Send resume events to all paused loops in 'loopmgr'. This can
+ * only be called by a running loop (which must therefore be the
+ * loop that called isc_loopmgr_pause()).
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ *\li We are in a running loop.
+ */
+
+uint32_t
+isc_loopmgr_nloops(isc_loopmgr_t *loopmgr);
+
+isc_job_t *
+isc_loop_setup(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
+isc_job_t *
+isc_loop_teardown(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
+/*%<
+ * Schedule actions to be run when starting, and when shutting down,
+ * one of the loops in a loop manager.
+ *
+ * Requires:
+ *\li 'loop' is a valid loop.
+ *\li The loop manager associated with 'loop' is paused or has not
+ * yet been started.
+ */
+
+void
+isc_loop_nosetup(isc_loop_t *loop, isc_job_t *job);
+void
+isc_loop_noteardown(isc_loop_t *loop, isc_job_t *job);
+
+void
+isc_loopmgr_setup(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg);
+void
+isc_loopmgr_teardown(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg);
+/*%<
+ * Schedule actions to be run when starting, and when shutting down,
+ * *all* of the loops in loopmgr.
+ *
+ * This is the same as running isc_loop_setup() or
+ * isc_loop_teardown() on each of the loops in turn.
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ *\li 'loopmgr' is paused or has not yet been started.
+ */
+
+isc_mem_t *
+isc_loop_getmctx(isc_loop_t *loop);
+/*%<
+ * Return a pointer to the a memory context that was created for
+ * 'loop' when it was initialized.
+ *
+ * Requires:
+ *\li 'loop' is a valid loop.
+ */
+
+isc_loop_t *
+isc_loop_main(isc_loopmgr_t *loopmgr);
+/*%<
+ * Returns the main loop for the 'loopmgr' (which is 'loopmgr->loops[0]',
+ * regardless of how many loops there are).
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ */
+
+isc_loop_t *
+isc_loop_current(isc_loopmgr_t *loopmgr);
+/*%<
+ * Returns the loop object from which the function has been called,
+ * or NULL if not called from a loop.
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ */
+
+isc_loop_t *
+isc_loop_get(isc_loopmgr_t *loopmgr, uint32_t tid);
+/*%<
+ * Return the loop object associated with the 'tid' threadid
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ *\li 'tid' is smaller than number of initialized loops
+ */
+
+void
+isc_loopmgr_blocking(isc_loopmgr_t *loopmgr);
+void
+isc_loopmgr_nonblocking(isc_loopmgr_t *loopmgr);
+/*%
+ * isc_loopmgr_blocking() stops the SIGINT and SIGTERM signal handlers
+ * during blocking operations, for example while waiting for user
+ * interaction; isc_loopmgr_nonblocking() restarts them.
+ *
+ * Requires:
+ *\li 'loopmgr' is a valid loop manager.
+ */
+ISC_LANG_ENDDECLS
ISC_INSIST(_refs > 0); \
} while (0)
+#define ISC_REFCOUNT_DECL(name) \
+ void name##_ref(name##_t *ptr); \
+ void name##_unref(name##_t *ptr); \
+ void name##_attach(name##_t *ptr, name##_t **ptrp); \
+ void name##_detach(name##_t **ptrp)
+
+#define ISC_REFCOUNT_IMPL(name, destroy) \
+ void name##_ref(name##_t *ptr) { \
+ REQUIRE(ptr != NULL); \
+ isc_refcount_increment(&ptr->references); \
+ } \
+ \
+ void name##_unref(name##_t *ptr) { \
+ REQUIRE(ptr != NULL); \
+ if (isc_refcount_decrement(&ptr->references) == 1) { \
+ destroy(ptr); \
+ } \
+ } \
+ void name##_attach(name##_t *ptr, name##_t **ptrp) { \
+ REQUIRE(ptrp != NULL && *ptrp == NULL); \
+ name##_ref(ptr); \
+ *ptrp = ptr; \
+ } \
+ \
+ void name##_detach(name##_t **ptrp) { \
+ name##_t *ptr = *ptrp; \
+ *ptrp = NULL; \
+ name##_unref(ptr); \
+ }
+
ISC_LANG_ENDDECLS
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include <isc/lang.h>
+#include <isc/loop.h>
+
+typedef void (*isc_signal_cb)(void *, int);
+
+ISC_LANG_BEGINDECLS
+
+isc_signal_t *
+isc_signal_new(isc_loopmgr_t *loopmgr, isc_signal_cb cb, void *cbarg,
+ int signum);
+/*%<
+ * Create a new signal handler for loop manager 'loopmgr', handling
+ * the signal value 'signum'.
+ *
+ * After isc_signal_start() is called on the returned signal handler,
+ * and until isc_signal_stop() is called, if the running process receives
+ * signal 'signum', 'cb' will be run with argument 'cbarg'.
+ */
+
+void
+isc_signal_destroy(isc_signal_t **signalp);
+/*%<
+ * Free the memory allocated by isc_signal_new().
+ */
+
+void
+isc_signal_start(isc_signal_t *signal);
+/*%<
+ * Start using the signal handler 'signal'.
+ */
+
+void
+isc_signal_stop(isc_signal_t *signal);
+/*%<
+ * Stop using the signal handler 'signal'. (It can be restarted with
+ * isc_signal_start().)
+ */
+ISC_LANG_ENDDECLS
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+#define ISC_TID_UNKNOWN UINT32_MAX
+
+uint32_t
+isc_tid(void);
+/*%<
+ * Returns the thread ID of the currently-running loop.
+ */
+
+/* Private */
+
+void
+isc__tid_init(uint32_t tid);
+
+ISC_LANG_ENDDECLS
typedef void(isc_httpdondestroy_t)(void *); /*%< Callback on destroying httpd */
typedef struct isc_interface isc_interface_t; /*%< Interface */
typedef struct isc_interfaceiter isc_interfaceiter_t; /*%< Interface Iterator */
-typedef struct isc_lex isc_lex_t; /*%< Lex */
-typedef struct isc_log isc_log_t; /*%< Log */
-typedef struct isc_logcategory isc_logcategory_t; /*%< Log Category */
-typedef struct isc_logconfig isc_logconfig_t; /*%< Log Configuration */
-typedef struct isc_logmodule isc_logmodule_t; /*%< Log Module */
-typedef struct isc_mem isc_mem_t; /*%< Memory */
-typedef struct isc_mempool isc_mempool_t; /*%< Memory Pool */
-typedef struct isc_netaddr isc_netaddr_t; /*%< Net Address */
-typedef struct isc_netprefix isc_netprefix_t; /*%< Net Prefix */
-typedef struct isc_nm isc_nm_t; /*%< Network manager */
+typedef struct isc_job isc_job_t;
+typedef struct isc_lex isc_lex_t; /*%< Lex */
+typedef struct isc_log isc_log_t; /*%< Log */
+typedef struct isc_logcategory isc_logcategory_t; /*%< Log Category */
+typedef struct isc_logconfig isc_logconfig_t; /*%< Log Configuration */
+typedef struct isc_logmodule isc_logmodule_t; /*%< Log Module */
+typedef struct isc_loop isc_loop_t; /*%< Event loop */
+typedef struct isc_loopmgr isc_loopmgr_t; /*%< Event loop manager */
+typedef struct isc_mem isc_mem_t; /*%< Memory */
+typedef struct isc_mempool isc_mempool_t; /*%< Memory Pool */
+typedef struct isc_netaddr isc_netaddr_t; /*%< Net Address */
+typedef struct isc_netprefix isc_netprefix_t; /*%< Net Prefix */
+typedef struct isc_nm isc_nm_t; /*%< Network manager */
typedef struct isc_nmsocket isc_nmsocket_t; /*%< Network manager socket */
typedef struct isc_nmhandle isc_nmhandle_t; /*%< Network manager handle */
typedef struct isc_portset isc_portset_t; /*%< Port Set */
typedef struct isc_ratelimiter isc_ratelimiter_t; /*%< Rate Limiter */
typedef struct isc_region isc_region_t; /*%< Region */
typedef uint64_t isc_resourcevalue_t; /*%< Resource Value */
+typedef struct isc_signal isc_signal_t; /*%< Signal handler */
typedef struct isc_sockaddr isc_sockaddr_t; /*%< Socket Address */
typedef ISC_LIST(isc_sockaddr_t) isc_sockaddrlist_t; /*%< Socket Address List
* */
typedef struct isc_time isc_time_t; /*%< Time */
typedef struct isc_timer isc_timer_t; /*%< Timer */
typedef struct isc_timermgr isc_timermgr_t; /*%< Timer Manager */
+typedef struct isc_work isc_work_t; /*%< Work offloaded to an
+ * external thread */
#if HAVE_LIBNGHTTP2
typedef struct isc_nm_http_endpoints isc_nm_http_endpoints_t;
#include <uv.h>
#include <isc/result.h>
+#include <isc/tid.h>
/*
* These functions were introduced in newer libuv, but we still
uv_handle_set_data((uv_handle_t *)(handle), (data))
#define uv_handle_get_data(handle) uv_handle_get_data((uv_handle_t *)(handle))
#define uv_close(handle, close_cb) uv_close((uv_handle_t *)handle, close_cb)
+
+#if UV_TRACE_INIT
+
+#define uv_idle_init(loop, idle) \
+ ({ \
+ int __r = uv_idle_init(loop, idle); \
+ fprintf(stderr, "%" PRIu32 ":%s_:uv_idle_init(%p, %p)\n", \
+ isc_tid(), __func__, loop, idle); \
+ __r; \
+ })
+
+#define uv_timer_init(loop, timer) \
+ ({ \
+ int __r = uv_timer_init(loop, timer); \
+ fprintf(stderr, "%" PRIu32 ":%s_:uv_timer_init(%p, %p)\n", \
+ isc_tid(), __func__, loop, timer); \
+ __r; \
+ })
+
+#define uv_async_init(loop, async, async_cb) \
+ ({ \
+ int __r = uv_async_init(loop, async, async_cb); \
+ fprintf(stderr, "%" PRIu32 ":%s_:uv_timer_init(%p, %p, %p)\n", \
+ isc_tid(), __func__, loop, async, async_cb); \
+ __r; \
+ })
+
+#define uv_close(handle, close_cb) \
+ ({ \
+ uv_close(handle, close_cb); \
+ fprintf(stderr, "%" PRIu32 ":%s_:uv_close(%p, %p)\n", \
+ isc_tid(), __func__, handle, close_cb); \
+ })
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include <isc/lang.h>
+#include <isc/loop.h>
+
+typedef void (*isc_work_cb)(void *arg);
+typedef void (*isc_after_work_cb)(void *arg);
+typedef struct isc_work isc_work_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_work_enqueue(isc_loop_t *loop, isc_work_cb work_cb,
+ isc_after_work_cb after_work_cb, void *cbarg);
+/*%<
+ * Schedules work to be handled by the libuv thread pool (see uv_work_t).
+ * The function specified in `work_cb` will be run by a thread in the
+ * thread pool; when complete, the `after_work_cb` function will run
+ * in 'loop' to inform the caller that the work was completed.
+ *
+ * Requires:
+ * \li 'loop' is a valid event loop.
+ * \li 'work_cb' and 'after_work_cb' are not NULL.
+ */
+
+ISC_LANG_ENDDECLS
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/condition.h>
+#include <isc/job.h>
+#include <isc/list.h>
+#include <isc/loop.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/signal.h>
+#include <isc/strerr.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+#include <isc/uv.h>
+#include <isc/work.h>
+
+#include "job_p.h"
+#include "loop_p.h"
+
+#define JOB_MAGIC ISC_MAGIC('J', 'O', 'B', ' ')
+#define VALID_JOB(t) ISC_MAGIC_VALID(t, JOB_MAGIC)
+
+/*
+ * Private: static
+ */
+
+static void
+isc__job_close_cb(uv_handle_t *handle) {
+ isc_job_t *job = uv_handle_get_data(handle);
+ isc_loop_t *loop = job->loop;
+
+ REQUIRE(loop == isc_loop_current(job->loop->loopmgr));
+
+ isc_mem_put(loop->mctx, job, sizeof(*job));
+
+ isc_loop_detach(&loop);
+}
+
+static void
+isc__job_destroy(isc_job_t *job) {
+ REQUIRE(VALID_JOB(job));
+ REQUIRE(job->loop == isc_loop_current(job->loop->loopmgr));
+
+ job->magic = 0;
+
+ uv_close(&job->idle, isc__job_close_cb);
+}
+
+static void
+isc__job_cb(uv_idle_t *idle) {
+ isc_job_t *job = uv_handle_get_data(idle);
+ int r;
+
+ REQUIRE(job->loop == isc_loop_current(job->loop->loopmgr));
+
+ job->cb(job->cbarg);
+
+ r = uv_idle_stop(idle);
+ UV_RUNTIME_CHECK(uv_idle_stop, r);
+
+ isc__job_destroy(job);
+}
+
+/*
+ * Public: #include <isc/job.h>
+ */
+
+void
+isc_job_run(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
+ isc_loop_t *loop = isc_loop_current(loopmgr);
+ isc_job_t *job = isc__job_new(loop, cb, cbarg);
+ isc__job_init(loop, job);
+ isc__job_run(job);
+}
+
+/*
+ * Protected: #include <job_p.h>
+ */
+
+isc_job_t *
+isc__job_new(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
+ isc_job_t *job = NULL;
+
+ REQUIRE(VALID_LOOP(loop));
+ REQUIRE(cb != NULL);
+
+ job = isc_mem_get(loop->mctx, sizeof(*job));
+ *job = (isc_job_t){
+ .magic = JOB_MAGIC,
+ .cb = cb,
+ .cbarg = cbarg,
+ };
+
+ isc_loop_attach(loop, &job->loop);
+
+ ISC_LINK_INIT(job, link);
+
+ return (job);
+}
+
+void
+isc__job_init(isc_loop_t *loop, isc_job_t *job) {
+ int r = uv_idle_init(&loop->loop, &job->idle);
+ UV_RUNTIME_CHECK(uv_idle_init, r);
+ uv_handle_set_data(&job->idle, job);
+}
+
+void
+isc__job_run(isc_job_t *job) {
+ int r;
+
+ REQUIRE(VALID_JOB(job));
+ REQUIRE(job->loop == isc_loop_current(job->loop->loopmgr));
+
+ r = uv_idle_start(&job->idle, isc__job_cb);
+ UV_RUNTIME_CHECK(uv_idle_start, r);
+}
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <isc/job.h>
+#include <isc/loop.h>
+
+isc_job_t *
+isc__job_new(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
+
+void
+isc__job_init(isc_loop_t *loop, isc_job_t *job);
+
+void
+isc__job_run(isc_job_t *job);
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/async.h>
+#include <isc/atomic.h>
+#include <isc/barrier.h>
+#include <isc/condition.h>
+#include <isc/job.h>
+#include <isc/list.h>
+#include <isc/loop.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/print.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/signal.h>
+#include <isc/strerr.h>
+#include <isc/thread.h>
+#include <isc/tid.h>
+#include <isc/util.h>
+#include <isc/uv.h>
+#include <isc/work.h>
+
+#include "job_p.h"
+#include "loop_p.h"
+
+/**
+ * Private
+ */
+
+static void
+ignore_signal(int sig, void (*handler)(int)) {
+ struct sigaction sa;
+
+ sa = (struct sigaction){ .sa_handler = handler };
+ if (sigfillset(&sa.sa_mask) != 0 || sigaction(sig, &sa, NULL) < 0) {
+ char strbuf[ISC_STRERRORSIZE];
+ strerror_r(errno, strbuf, sizeof(strbuf));
+ isc_error_fatal(__FILE__, __LINE__, "%s() %d setup: %s",
+ __func__, sig, strbuf);
+ }
+}
+
+void
+isc_loopmgr_shutdown(isc_loopmgr_t *loopmgr) {
+ if (!atomic_compare_exchange_strong(&loopmgr->shuttingdown,
+ &(bool){ false }, true))
+ {
+ return;
+ }
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ int r;
+
+ REQUIRE(!atomic_load(&loop->finished));
+
+ r = uv_async_send(&loop->shutdown_trigger);
+ UV_RUNTIME_CHECK(uv_async_send, r);
+ }
+}
+
+static void
+isc__loopmgr_signal(void *arg, int signum) {
+ isc_loopmgr_t *loopmgr = (isc_loopmgr_t *)arg;
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ isc_loopmgr_shutdown(loopmgr);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+static void
+pause_loop(isc_loop_t *loop) {
+ isc_loopmgr_t *loopmgr = loop->loopmgr;
+
+ loop->paused = true;
+ (void)isc_barrier_wait(&loopmgr->pausing);
+}
+
+static void
+resume_loop(isc_loop_t *loop) {
+ isc_loopmgr_t *loopmgr = loop->loopmgr;
+
+ (void)isc_barrier_wait(&loopmgr->resuming);
+ loop->paused = false;
+}
+
+static void
+pauseresume_cb(uv_async_t *handle) {
+ isc_loop_t *loop = uv_handle_get_data(handle);
+
+ pause_loop(loop);
+ resume_loop(loop);
+}
+
+#define XX(uc, lc) \
+ case UV_##uc: \
+ fprintf(stderr, "%s, %s: dangling %p: %p.type = %s\n", \
+ __func__, (char *)arg, handle->loop, handle, #lc); \
+ break;
+
+static void
+loop_walk_cb(uv_handle_t *handle, void *arg) {
+ if (uv_is_closing(handle)) {
+ return;
+ }
+
+ switch (handle->type) {
+ UV_HANDLE_TYPE_MAP(XX)
+ default:
+ fprintf(stderr, "%s, %s: dangling %p: %p.type = %s\n", __func__,
+ (char *)arg, &handle->loop, handle, "unknown");
+ }
+}
+
+static void
+shutdown_trigger_close_cb(uv_handle_t *handle) {
+ isc_loop_t *loop = uv_handle_get_data(handle);
+
+ isc_loop_detach(&loop);
+}
+
+static void
+destroy_cb(uv_async_t *handle) {
+ isc_loop_t *loop = uv_handle_get_data(handle);
+
+ uv_close(&loop->destroy_trigger, NULL);
+ uv_close(&loop->queue_trigger, NULL);
+ uv_close(&loop->pause_trigger, NULL);
+
+ uv_walk(&loop->loop, loop_walk_cb, (char *)"destroy_cb");
+}
+
+static void
+shutdown_cb(uv_async_t *handle) {
+ isc_job_t *job = NULL;
+ isc_loop_t *loop = uv_handle_get_data(handle);
+ isc_loopmgr_t *loopmgr = loop->loopmgr;
+
+ /* Make sure, we can't be called again */
+ uv_close(&loop->shutdown_trigger, shutdown_trigger_close_cb);
+
+ if (DEFAULT_LOOP(loopmgr) == CURRENT_LOOP(loopmgr)) {
+ /* Stop the signal handlers */
+ isc_signal_stop(loopmgr->sigterm);
+ isc_signal_stop(loopmgr->sigint);
+
+ /* Free the signal handlers */
+ isc_signal_destroy(&loopmgr->sigterm);
+ isc_signal_destroy(&loopmgr->sigint);
+ }
+
+ job = ISC_LIST_TAIL(loop->teardown_jobs);
+ while (job != NULL) {
+ isc_job_t *prev = ISC_LIST_PREV(job, link);
+ ISC_LIST_UNLINK(loop->teardown_jobs, job, link);
+
+ isc__job_run(job);
+
+ job = prev;
+ }
+}
+
+static void
+queue_cb(uv_async_t *handle) {
+ isc_loop_t *loop = uv_handle_get_data(handle);
+ isc_job_t *job = NULL;
+ ISC_LIST(isc_job_t) list;
+
+ REQUIRE(VALID_LOOP(loop));
+
+ ISC_LIST_INIT(list);
+
+ LOCK(&loop->queue_lock);
+ ISC_LIST_MOVE(list, loop->queue_jobs);
+ UNLOCK(&loop->queue_lock);
+
+ job = ISC_LIST_HEAD(list);
+ while (job != NULL) {
+ isc_job_t *next = ISC_LIST_NEXT(job, link);
+ ISC_LIST_UNLINK(list, job, link);
+
+ isc__job_init(loop, job);
+ isc__job_run(job);
+
+ job = next;
+ }
+}
+
+static void
+loop_init(isc_loop_t *loop, isc_loopmgr_t *loopmgr, size_t tid) {
+ *loop = (isc_loop_t){
+ .tid = tid,
+ .loopmgr = loopmgr,
+ };
+
+ int r = uv_loop_init(&loop->loop);
+ UV_RUNTIME_CHECK(uv_loop_init, r);
+
+ r = uv_async_init(&loop->loop, &loop->pause_trigger, pauseresume_cb);
+ UV_RUNTIME_CHECK(uv_async_init, r);
+ uv_handle_set_data(&loop->pause_trigger, loop);
+
+ r = uv_async_init(&loop->loop, &loop->shutdown_trigger, shutdown_cb);
+ UV_RUNTIME_CHECK(uv_async_init, r);
+ uv_handle_set_data(&loop->shutdown_trigger, loop);
+
+ r = uv_async_init(&loop->loop, &loop->queue_trigger, queue_cb);
+ UV_RUNTIME_CHECK(uv_async_init, r);
+ uv_handle_set_data(&loop->queue_trigger, loop);
+
+ r = uv_async_init(&loop->loop, &loop->destroy_trigger, destroy_cb);
+ UV_RUNTIME_CHECK(uv_async_init, r);
+ uv_handle_set_data(&loop->destroy_trigger, loop);
+
+ isc_mem_create(&loop->mctx);
+
+ isc_mutex_init(&loop->queue_lock);
+
+ ISC_LIST_INIT(loop->queue_jobs);
+ ISC_LIST_INIT(loop->setup_jobs);
+ ISC_LIST_INIT(loop->teardown_jobs);
+
+ isc_refcount_init(&loop->references, 1);
+
+ loop->magic = LOOP_MAGIC;
+}
+
+static void
+loop_run(isc_loop_t *loop) {
+ int r;
+ isc_job_t *job;
+
+ job = ISC_LIST_HEAD(loop->setup_jobs);
+ while (job != NULL) {
+ isc_job_t *next = ISC_LIST_NEXT(job, link);
+ ISC_LIST_UNLINK(loop->setup_jobs, job, link);
+
+ isc__job_run(job);
+
+ job = next;
+ }
+
+ isc_barrier_wait(&loop->loopmgr->starting);
+
+ r = uv_run(&loop->loop, UV_RUN_DEFAULT);
+ UV_RUNTIME_CHECK(uv_run, r);
+
+ isc_barrier_wait(&loop->loopmgr->stopping);
+}
+
+static void
+loop_close(isc_loop_t *loop) {
+ int r = uv_loop_close(&loop->loop);
+ UV_RUNTIME_CHECK(uv_loop_close, r);
+
+ isc_mutex_destroy(&loop->queue_lock);
+ INSIST(ISC_LIST_EMPTY(loop->queue_jobs));
+
+ loop->magic = 0;
+
+ isc_mem_detach(&loop->mctx);
+}
+
+static isc_threadresult_t
+loop_thread(isc_threadarg_t arg) {
+ isc_loop_t *loop = (isc_loop_t *)arg;
+
+ /* Initialize the thread_local variable */
+
+ isc__tid_init(loop->tid);
+
+ loop_run(loop);
+
+ return ((isc_threadresult_t)0);
+}
+
+void
+isc_loop_nosetup(isc_loop_t *loop, isc_job_t *job) {
+ ISC_LIST_DEQUEUE(loop->setup_jobs, job, link);
+}
+
+void
+isc_loop_noteardown(isc_loop_t *loop, isc_job_t *job) {
+ ISC_LIST_DEQUEUE(loop->teardown_jobs, job, link);
+}
+
+/**
+ * Public
+ */
+
+static void
+threadpool_initialize(uint32_t workers) {
+ char buf[11];
+ int r = uv_os_getenv("UV_THREADPOOL_SIZE", buf,
+ &(size_t){ sizeof(buf) });
+ if (r == UV_ENOENT) {
+ snprintf(buf, sizeof(buf), "%" PRIu32, workers);
+ uv_os_setenv("UV_THREADPOOL_SIZE", buf);
+ }
+}
+
+static void
+loop_destroy(isc_loop_t *loop) {
+ int r = uv_async_send(&loop->destroy_trigger);
+ UV_RUNTIME_CHECK(uv_async_send, r);
+}
+
+ISC_REFCOUNT_IMPL(isc_loop, loop_destroy);
+
+void
+isc_loopmgr_create(isc_mem_t *mctx, uint32_t nloops, isc_loopmgr_t **loopmgrp) {
+ isc_loopmgr_t *loopmgr = NULL;
+
+ REQUIRE(loopmgrp != NULL && *loopmgrp == NULL);
+ REQUIRE(nloops > 0);
+
+ threadpool_initialize(nloops);
+
+ loopmgr = isc_mem_get(mctx, sizeof(*loopmgr));
+ *loopmgr = (isc_loopmgr_t){
+ .nloops = nloops,
+ };
+
+ isc_mem_attach(mctx, &loopmgr->mctx);
+
+ isc_barrier_init(&loopmgr->pausing, loopmgr->nloops);
+ isc_barrier_init(&loopmgr->resuming, loopmgr->nloops);
+ isc_barrier_init(&loopmgr->starting, loopmgr->nloops);
+ isc_barrier_init(&loopmgr->stopping, loopmgr->nloops);
+
+ loopmgr->loops = isc_mem_get(
+ loopmgr->mctx, loopmgr->nloops * sizeof(loopmgr->loops[0]));
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ loop_init(loop, loopmgr, i);
+ }
+
+ loopmgr->sigint = isc_signal_new(loopmgr, isc__loopmgr_signal, loopmgr,
+ SIGINT);
+ loopmgr->sigterm = isc_signal_new(loopmgr, isc__loopmgr_signal, loopmgr,
+ SIGTERM);
+
+ isc_signal_start(loopmgr->sigint);
+ isc_signal_start(loopmgr->sigterm);
+
+ loopmgr->magic = LOOPMGR_MAGIC;
+
+ *loopmgrp = loopmgr;
+}
+
+isc_job_t *
+isc_loop_setup(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
+ isc_loopmgr_t *loopmgr = NULL;
+ isc_job_t *job = NULL;
+
+ REQUIRE(VALID_LOOP(loop));
+ REQUIRE(cb != NULL);
+
+ loopmgr = loop->loopmgr;
+
+ REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
+ atomic_load(&loopmgr->paused));
+
+ job = isc__job_new(loop, cb, cbarg);
+ isc__job_init(loop, job);
+
+ /*
+ * The ISC_LIST_PREPEND is counterintuitive here, but actually, the
+ * uv_idle_start() puts the item on the HEAD of the internal list, so we
+ * want to store items here in reverse order, so on the uv_loop, they
+ * are scheduled in the correct order
+ */
+ ISC_LIST_PREPEND(loop->setup_jobs, job, link);
+
+ return (job);
+}
+
+isc_job_t *
+isc_loop_teardown(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
+ isc_loopmgr_t *loopmgr = NULL;
+ isc_job_t *job = NULL;
+
+ REQUIRE(VALID_LOOP(loop));
+
+ loopmgr = loop->loopmgr;
+
+ REQUIRE(loop->tid == isc_tid() || !atomic_load(&loopmgr->running) ||
+ atomic_load(&loopmgr->paused));
+
+ job = isc__job_new(loop, cb, cbarg);
+ isc__job_init(loop, job);
+
+ /*
+ * The ISC_LIST_PREPEND is counterintuitive here, but actually, the
+ * uv_idle_start() puts the item on the HEAD of the internal list, so we
+ * want to store items here in reverse order, so on the uv_loop, they
+ * are scheduled in the correct order
+ */
+ ISC_LIST_PREPEND(loop->teardown_jobs, job, link);
+
+ return (job);
+}
+
+void
+isc_loopmgr_setup(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+ REQUIRE(!atomic_load(&loopmgr->running) ||
+ atomic_load(&loopmgr->paused));
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ (void)isc_loop_setup(loop, cb, cbarg);
+ }
+}
+
+void
+isc_loopmgr_teardown(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+ REQUIRE(!atomic_load(&loopmgr->running) ||
+ atomic_load(&loopmgr->paused));
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ (void)isc_loop_teardown(loop, cb, cbarg);
+ }
+}
+
+void
+isc_loopmgr_run(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+ RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->running,
+ &(bool){ false }, true));
+
+ /*
+ * Always ignore SIGPIPE.
+ */
+ ignore_signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * The thread 0 is this one.
+ */
+ for (size_t i = 1; i < loopmgr->nloops; i++) {
+ char name[32];
+ isc_loop_t *loop = &loopmgr->loops[i];
+
+ isc_thread_create(loop_thread, loop, &loop->thread);
+
+ snprintf(name, sizeof(name), "isc-loop-%04zu", i);
+ isc_thread_setname(loop->thread, name);
+ }
+
+ loop_thread(&loopmgr->loops[0]);
+}
+
+void
+isc_loopmgr_pause(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+
+ /* Skip current loop */
+ if (i == isc_tid()) {
+ continue;
+ }
+
+ REQUIRE(!atomic_load(&loop->finished));
+ uv_async_send(&loop->pause_trigger);
+ }
+
+ RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->paused,
+ &(bool){ false }, true));
+ pause_loop(CURRENT_LOOP(loopmgr));
+}
+
+void
+isc_loopmgr_resume(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->paused,
+ &(bool){ true }, false));
+ resume_loop(CURRENT_LOOP(loopmgr));
+}
+
+void
+isc_loopmgr_destroy(isc_loopmgr_t **loopmgrp) {
+ isc_loopmgr_t *loopmgr = NULL;
+
+ REQUIRE(loopmgrp != NULL);
+ REQUIRE(VALID_LOOPMGR(*loopmgrp));
+
+ loopmgr = *loopmgrp;
+ *loopmgrp = NULL;
+
+ RUNTIME_CHECK(atomic_compare_exchange_strong(&loopmgr->running,
+ &(bool){ true }, false));
+
+ /* First wait for all loops to finish */
+ for (size_t i = 1; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ isc_thread_join(loop->thread, NULL);
+ }
+
+ loopmgr->magic = 0;
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+ loop_close(loop);
+ }
+ isc_mem_put(loopmgr->mctx, loopmgr->loops,
+ loopmgr->nloops * sizeof(loopmgr->loops[0]));
+
+ isc_barrier_destroy(&loopmgr->starting);
+ isc_barrier_destroy(&loopmgr->stopping);
+ isc_barrier_destroy(&loopmgr->resuming);
+ isc_barrier_destroy(&loopmgr->pausing);
+
+ isc_mem_putanddetach(&loopmgr->mctx, loopmgr, sizeof(*loopmgr));
+}
+
+uint32_t
+isc_loopmgr_nloops(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ return (loopmgr->nloops);
+}
+
+isc_mem_t *
+isc_loop_getmctx(isc_loop_t *loop) {
+ REQUIRE(VALID_LOOP(loop));
+
+ return (loop->mctx);
+}
+
+isc_loop_t *
+isc_loop_main(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ return (DEFAULT_LOOP(loopmgr));
+}
+
+isc_loop_t *
+isc_loop_current(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ return (CURRENT_LOOP(loopmgr));
+}
+
+isc_loop_t *
+isc_loop_get(isc_loopmgr_t *loopmgr, uint32_t tid) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+ REQUIRE(tid < loopmgr->nloops);
+
+ return (LOOP(loopmgr, tid));
+}
+
+void
+isc_loopmgr_blocking(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ isc_signal_stop(loopmgr->sigterm);
+ isc_signal_stop(loopmgr->sigint);
+}
+
+void
+isc_loopmgr_nonblocking(isc_loopmgr_t *loopmgr) {
+ REQUIRE(VALID_LOOPMGR(loopmgr));
+
+ isc_signal_start(loopmgr->sigint);
+ isc_signal_start(loopmgr->sigterm);
+}
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <isc/barrier.h>
+#include <isc/lang.h>
+#include <isc/loop.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/refcount.h>
+#include <isc/result.h>
+#include <isc/signal.h>
+#include <isc/thread.h>
+#include <isc/types.h>
+#include <isc/uv.h>
+#include <isc/work.h>
+
+/*
+ * Per-thread loop
+ */
+#define LOOP_MAGIC ISC_MAGIC('L', 'O', 'O', 'P')
+#define VALID_LOOP(t) ISC_MAGIC_VALID(t, LOOP_MAGIC)
+
+typedef ISC_LIST(isc_job_t) isc_joblist_t;
+
+struct isc_loop {
+ int magic;
+ isc_refcount_t references;
+ isc_thread_t thread;
+
+ isc_loopmgr_t *loopmgr;
+
+ uv_loop_t loop;
+ uint32_t tid;
+
+ isc_mem_t *mctx;
+
+ /* states */
+ bool paused;
+ atomic_bool finished;
+ bool shuttingdown;
+
+ /* Async queue */
+ uv_async_t queue_trigger;
+ isc_mutex_t queue_lock;
+ isc_joblist_t queue_jobs;
+
+ /* Pause */
+ uv_async_t pause_trigger;
+
+ /* Shutdown */
+ uv_async_t shutdown_trigger;
+ isc_joblist_t setup_jobs;
+ isc_joblist_t teardown_jobs;
+
+ /* Destroy */
+ uv_async_t destroy_trigger;
+};
+
+/*
+ * Loop Manager
+ */
+#define LOOPMGR_MAGIC ISC_MAGIC('L', 'o', 'o', 'M')
+#define VALID_LOOPMGR(t) ISC_MAGIC_VALID(t, LOOPMGR_MAGIC)
+
+struct isc_loopmgr {
+ int magic;
+ isc_mem_t *mctx;
+
+ uint_fast32_t nloops;
+
+ atomic_bool shuttingdown;
+ atomic_bool running;
+ atomic_bool paused;
+
+ /* signal handling */
+ isc_signal_t *sigint;
+ isc_signal_t *sigterm;
+
+ /* pause/resume */
+ isc_barrier_t pausing;
+ isc_barrier_t resuming;
+
+ /* start/stop */
+ isc_barrier_t starting;
+
+ /* stopping */
+ isc_barrier_t stopping;
+
+ /* per-thread objects */
+ isc_loop_t *loops;
+};
+
+/*
+ * Signal Handler
+ */
+struct isc_signal {
+ uv_signal_t signal;
+ isc_loop_t *loop;
+ isc_signal_cb cb;
+ void *cbarg;
+ int signum;
+};
+
+/*
+ * Job to be scheduled in an event loop
+ */
+#define JOB_MAGIC ISC_MAGIC('J', 'O', 'B', ' ')
+#define VALID_JOB(t) ISC_MAGIC_VALID(t, JOB_MAGIC)
+
+struct isc_job {
+ int magic;
+ uv_idle_t idle;
+ isc_loop_t *loop;
+ isc_job_cb cb;
+ void *cbarg;
+ LINK(isc_job_t) link;
+};
+
+/*
+ * Work to be offloaded to an external thread.
+ */
+struct isc_work {
+ uv_work_t work;
+ isc_loop_t *loop;
+ isc_work_cb work_cb;
+ isc_after_work_cb after_work_cb;
+ void *cbarg;
+};
+
+#define DEFAULT_LOOP(loopmgr) (&(loopmgr)->loops[0])
+#define CURRENT_LOOP(loopmgr) (&(loopmgr)->loops[isc_tid()])
+#define LOOP(loopmgr, tid) (&(loopmgr)->loops[tid])
+#define ON_LOOP(loop) ((loop) == CURRENT_LOOP((loop)->loopmgr))
+
+ISC_REFCOUNT_DECL(isc_loop);
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+
+#include <isc/loop.h>
+#include <isc/signal.h>
+#include <isc/uv.h>
+
+#include "loop_p.h"
+
+isc_signal_t *
+isc_signal_new(isc_loopmgr_t *loopmgr, isc_signal_cb cb, void *cbarg,
+ int signum) {
+ isc_loop_t *loop = NULL;
+ isc_signal_t *signal = NULL;
+ int r;
+
+ loop = DEFAULT_LOOP(loopmgr);
+
+ signal = isc_mem_get(isc_loop_getmctx(loop), sizeof(*signal));
+ *signal = (isc_signal_t){
+ .cb = cb,
+ .cbarg = cbarg,
+ .signum = signum,
+ };
+
+ isc_loop_attach(loop, &signal->loop);
+
+ r = uv_signal_init(&loop->loop, &signal->signal);
+ UV_RUNTIME_CHECK(uv_signal_init, r);
+
+ uv_handle_set_data((uv_handle_t *)&signal->signal, signal);
+
+ return (signal);
+}
+
+static void
+isc__signal_destroy_cb(uv_handle_t *handle) {
+ isc_signal_t *signal = uv_handle_get_data(handle);
+ isc_loop_t *loop = signal->loop;
+
+ isc_mem_put(loop->mctx, signal, sizeof(*signal));
+
+ isc_loop_detach(&loop);
+}
+
+void
+isc_signal_destroy(isc_signal_t **signalp) {
+ isc_signal_t *signal;
+
+ REQUIRE(signalp != NULL && *signalp != NULL);
+ signal = *signalp;
+ *signalp = NULL;
+
+ uv_close((uv_handle_t *)&signal->signal, isc__signal_destroy_cb);
+}
+
+void
+isc_signal_stop(isc_signal_t *signal) {
+ int r = uv_signal_stop(&signal->signal);
+ UV_RUNTIME_CHECK(uv_signal_stop, r);
+}
+
+static void
+isc__signal_cb(uv_signal_t *handle, int signum) {
+ isc_signal_t *signal = uv_handle_get_data((uv_handle_t *)handle);
+
+ REQUIRE(signum == signal->signum);
+
+ signal->cb(signal->cbarg, signum);
+}
+
+void
+isc_signal_start(isc_signal_t *signal) {
+ int r = uv_signal_start(&signal->signal, isc__signal_cb,
+ signal->signum);
+ UV_RUNTIME_CHECK(uv_signal_start, r);
+}
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <isc/lang.h>
+#include <isc/tid.h>
+#include <isc/util.h>
+
+/**
+ * Private
+ */
+
+#define ISC_TID_UNKNOWN UINT32_MAX
+
+static thread_local uint32_t isc__tid_v = ISC_TID_UNKNOWN;
+
+/**
+ * Protected
+ */
+
+void
+isc__tid_init(uint32_t tid) {
+ REQUIRE(isc__tid_v == ISC_TID_UNKNOWN || isc__tid_v == tid);
+
+ isc__tid_v = tid;
+}
+
+/**
+ * Public
+ */
+
+uint32_t
+isc_tid(void) {
+ return (isc__tid_v);
+}
#include <inttypes.h>
#include <stdlib.h>
-#include <uv.h>
#include <isc/mem.h>
#include <isc/once.h>
#include <isc/thread.h>
#include <isc/util.h>
+#include <isc/uv.h>
#include "trampoline_p.h"
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <stdlib.h>
+
+#include <isc/job.h>
+#include <isc/loop.h>
+#include <isc/uv.h>
+#include <isc/work.h>
+
+#include "loop_p.h"
+
+static void
+isc__work_cb(uv_work_t *req) {
+ isc_work_t *work = uv_req_get_data((uv_req_t *)req);
+
+ work->work_cb(work->cbarg);
+}
+
+static void
+isc__after_work_cb(uv_work_t *req, int status) {
+ isc_work_t *work = uv_req_get_data((uv_req_t *)req);
+ isc_loop_t *loop = work->loop;
+
+ UV_RUNTIME_CHECK(uv_after_work_cb, status);
+
+ work->after_work_cb(work->cbarg);
+
+ isc_mem_put(loop->mctx, work, sizeof(*work));
+
+ isc_loop_detach(&loop);
+}
+
+void
+isc_work_enqueue(isc_loop_t *loop, isc_work_cb work_cb,
+ isc_after_work_cb after_work_cb, void *cbarg) {
+ isc_work_t *work = NULL;
+ int r;
+
+ REQUIRE(VALID_LOOP(loop));
+ REQUIRE(work_cb != NULL);
+ REQUIRE(after_work_cb != NULL);
+
+ work = isc_mem_get(loop->mctx, sizeof(*work));
+ *work = (isc_work_t){
+ .work_cb = work_cb,
+ .after_work_cb = after_work_cb,
+ .cbarg = cbarg,
+ };
+
+ isc_loop_attach(loop, &work->loop);
+
+ uv_req_set_data((uv_req_t *)&work->work, work);
+
+ r = uv_queue_work(&loop->loop, &work->work, isc__work_cb,
+ isc__after_work_cb);
+ UV_RUNTIME_CHECK(uv_queue_work, r);
+}
extern isc_mem_t *mctx;
extern isc_nm_t *netmgr;
+extern isc_loopmgr_t *loopmgr;
+extern isc_loop_t *mainloop;
extern isc_taskmgr_t *taskmgr;
extern isc_timermgr_t *timermgr;
extern unsigned int workers;
#define isc_test_nap(ms) uv_sleep(ms)
+int
+setup_mctx(void **state);
+int
+teardown_mctx(void **state);
+
+int
+setup_loopmgr(void **state);
+int
+teardown_loopmgr(void **state);
+
int
setup_managers(void **state);
int
check_PROGRAMS = \
aes_test \
+ async_test \
buffer_test \
counter_test \
crc64_test \
heap_test \
hmac_test \
ht_test \
+ job_test \
lex_test \
+ loop_test \
md_test \
mem_test \
netaddr_test \
symtab_test \
task_test \
time_test \
- timer_test
+ timer_test \
+ work_test
if HAVE_LIBNGHTTP2
check_PROGRAMS += \
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/async.h>
+#include <isc/atomic.h>
+#include <isc/loop.h>
+#include <isc/os.h>
+#include <isc/result.h>
+#include <isc/tid.h>
+#include <isc/util.h>
+
+#include "async.c"
+
+#include <tests/isc.h>
+
+static atomic_uint scheduled = 0;
+
+static void
+async_cb(void *arg) {
+ UNUSED(arg);
+ uint32_t tid = isc_tid();
+
+ atomic_fetch_add(&scheduled, 1);
+
+ if (tid > 0) {
+ isc_loop_t *loop = isc_loop_get(loopmgr, tid - 1);
+ isc_async_run(loop, async_cb, loopmgr);
+ } else {
+ isc_loopmgr_shutdown(loopmgr);
+ }
+}
+
+static void
+async_setup_cb(void *arg) {
+ UNUSED(arg);
+ uint32_t tid = isc_loopmgr_nloops(loopmgr) - 1;
+
+ isc_loop_t *loop = isc_loop_get(loopmgr, tid);
+
+ isc_async_run(loop, async_cb, loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_async_run) {
+ isc_loop_setup(isc_loop_main(loopmgr), async_setup_cb, loopmgr);
+ isc_loopmgr_run(loopmgr);
+ assert_int_equal(atomic_load(&scheduled), loopmgr->nloops);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(isc_async_run, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/atomic.h>
+#include <isc/job.h>
+#include <isc/loop.h>
+#include <isc/os.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include "job.c"
+
+#include <tests/isc.h>
+
+static atomic_uint scheduled;
+static atomic_uint executed;
+
+#define MAX_EXECUTED 1000000
+
+static void
+shutdown_cb(void *arg) {
+ UNUSED(arg);
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+static void
+job_cb(void *arg __attribute__((__unused__))) {
+ unsigned int n = atomic_fetch_add(&executed, 1);
+
+ if (n <= MAX_EXECUTED) {
+ atomic_fetch_add(&scheduled, 1);
+ isc_job_run(loopmgr, job_cb, loopmgr);
+ } else {
+ isc_job_run(loopmgr, shutdown_cb, loopmgr);
+ }
+}
+
+static void
+job_run_cb(void *arg __attribute__((__unused__))) {
+ atomic_fetch_add(&scheduled, 1);
+
+ isc_job_run(loopmgr, job_cb, loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_job_run) {
+ atomic_init(&scheduled, 0);
+ atomic_init(&executed, 0);
+
+ isc_loopmgr_setup(loopmgr, job_run_cb, loopmgr);
+
+ isc_loopmgr_run(loopmgr);
+
+ assert_int_equal(atomic_load(&scheduled), atomic_load(&executed));
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(isc_job_run, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/atomic.h>
+#include <isc/loop.h>
+#include <isc/os.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include "loop.c"
+
+#include <tests/isc.h>
+
+static atomic_uint scheduled = 0;
+
+static void
+count(void *arg) {
+ UNUSED(arg);
+
+ atomic_fetch_add(&scheduled, 1);
+}
+
+static void
+shutdown_loopmgr(void *arg) {
+ UNUSED(arg);
+
+ while (atomic_load(&scheduled) != loopmgr->nloops) {
+ isc_thread_yield();
+ }
+
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_loopmgr) {
+ atomic_store(&scheduled, 0);
+
+ isc_loopmgr_setup(loopmgr, count, loopmgr);
+ isc_loop_setup(mainloop, shutdown_loopmgr, loopmgr);
+
+ isc_loopmgr_run(loopmgr);
+
+ assert_int_equal(atomic_load(&scheduled), loopmgr->nloops);
+}
+
+static void
+runjob(void *arg __attribute__((__unused__))) {
+ if (isc_tid() == 0) {
+ isc_job_run(loopmgr, shutdown_loopmgr, loopmgr);
+ }
+
+ isc_job_run(loopmgr, count, loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_loopmgr_runjob) {
+ atomic_store(&scheduled, 0);
+
+ isc_loopmgr_setup(loopmgr, runjob, loopmgr);
+ isc_loopmgr_run(loopmgr);
+ assert_int_equal(atomic_load(&scheduled), loopmgr->nloops);
+}
+
+static void
+pause_loopmgr(void *arg) {
+ UNUSED(arg);
+
+ isc_loopmgr_pause(loopmgr);
+
+ assert_true(atomic_load(&loopmgr->paused));
+
+ for (size_t i = 0; i < loopmgr->nloops; i++) {
+ isc_loop_t *loop = &loopmgr->loops[i];
+
+ assert_true(loop->paused);
+ }
+
+ atomic_init(&scheduled, loopmgr->nloops);
+
+ isc_loopmgr_resume(loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_loopmgr_pause) {
+ isc_loop_setup(mainloop, pause_loopmgr, loopmgr);
+ isc_loop_setup(mainloop, shutdown_loopmgr, loopmgr);
+ isc_loopmgr_run(loopmgr);
+}
+
+static void
+send_sigint(void *arg) {
+ UNUSED(arg);
+
+ kill(getpid(), SIGINT);
+}
+
+ISC_RUN_TEST_IMPL(isc_loopmgr_sigint) {
+ isc_loop_setup(mainloop, send_sigint, loopmgr);
+ isc_loopmgr_run(loopmgr);
+}
+
+static void
+send_sigterm(void *arg) {
+ UNUSED(arg);
+
+ kill(getpid(), SIGINT);
+}
+
+ISC_RUN_TEST_IMPL(isc_loopmgr_sigterm) {
+ isc_loop_setup(mainloop, send_sigterm, loopmgr);
+ isc_loopmgr_run(loopmgr);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(isc_loopmgr, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_ENTRY_CUSTOM(isc_loopmgr_pause, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_ENTRY_CUSTOM(isc_loopmgr_runjob, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_ENTRY_CUSTOM(isc_loopmgr_sigint, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_ENTRY_CUSTOM(isc_loopmgr_sigterm, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/atomic.h>
+#include <isc/loop.h>
+#include <isc/os.h>
+#include <isc/result.h>
+#include <isc/tid.h>
+#include <isc/util.h>
+#include <isc/work.h>
+
+#include "work.c"
+
+#include <tests/isc.h>
+
+static atomic_uint scheduled = 0;
+
+static void
+work_cb(void *arg) {
+ UNUSED(arg);
+
+ atomic_fetch_add(&scheduled, 1);
+
+ assert_int_equal(isc_tid(), UINT32_MAX);
+}
+
+static void
+after_work_cb(void *arg) {
+ UNUSED(arg);
+
+ assert_int_equal(atomic_load(&scheduled), 1);
+ isc_loopmgr_shutdown(loopmgr);
+}
+
+static void
+work_enqueue_cb(void *arg) {
+ UNUSED(arg);
+ uint32_t tid = isc_loopmgr_nloops(loopmgr) - 1;
+
+ isc_loop_t *loop = isc_loop_get(loopmgr, tid);
+
+ isc_work_enqueue(loop, work_cb, after_work_cb, loopmgr);
+}
+
+ISC_RUN_TEST_IMPL(isc_work_enqueue) {
+ atomic_init(&scheduled, 0);
+
+ isc_loop_setup(isc_loop_main(loopmgr), work_enqueue_cb, loopmgr);
+
+ isc_loopmgr_run(loopmgr);
+
+ assert_int_equal(atomic_load(&scheduled), 1);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(isc_work_enqueue, setup_loopmgr, teardown_loopmgr)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
#include <isc/buffer.h>
#include <isc/hash.h>
+#include <isc/loop.h>
#include <isc/managers.h>
#include <isc/mem.h>
#include <isc/os.h>
#include <tests/isc.h>
isc_mem_t *mctx = NULL;
+isc_loopmgr_t *loopmgr = NULL;
+isc_loop_t *mainloop = NULL;
isc_taskmgr_t *taskmgr = NULL;
isc_timermgr_t *timermgr = NULL;
isc_nm_t *netmgr = NULL;
unsigned int workers = 0;
isc_task_t *maintask = NULL;
+int
+setup_mctx(void **state __attribute__((__unused__))) {
+ isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
+ isc_mem_create(&mctx);
+
+ return (0);
+}
+
+int
+teardown_mctx(void **state __attribute__((__unused__))) {
+ isc_mem_destroy(&mctx);
+
+ return (0);
+}
+
+int
+setup_loopmgr(void **state __attribute__((__unused__))) {
+ char *env_workers = NULL;
+
+ REQUIRE(mctx != NULL);
+
+ env_workers = getenv("ISC_TASK_WORKERS");
+ if (env_workers != NULL) {
+ workers = atoi(env_workers);
+ } else {
+ /* We always need at least two loops for some of the tests */
+ workers = isc_os_ncpus() + 1;
+ }
+ INSIST(workers != 0);
+
+ isc_loopmgr_create(mctx, workers, &loopmgr);
+ mainloop = isc_loop_main(loopmgr);
+
+ return (0);
+}
+
+int
+teardown_loopmgr(void **state __attribute__((__unused__))) {
+ REQUIRE(taskmgr == NULL);
+ REQUIRE(netmgr == NULL);
+
+ mainloop = NULL;
+ isc_loopmgr_destroy(&loopmgr);
+
+ return (0);
+}
+
int
setup_managers(void **state) {
isc_result_t result;