New event loop handling API

This commit introduces new APIs for applications and signal handling,
intended to replace isc_app for applications built on top of libisc.

* isc_app will be replaced with isc_loopmgr, which handles the
  starting and stopping of applications. In isc_loopmgr, the main
  thread is not blocked, but is part of the working thread set.
  The loop manager will start a number of threads, each with a
  uv_loop event loop running. Setup and teardown functions can be
  assigned which will run when the loop starts and stops, and
  jobs can be scheduled to run in the meantime. When
  isc_loopmgr_shutdown() is run from any the loops, all loops
  will shut down and the application can terminate.

* signal handling will now be handled with a separate isc_signal unit.
  isc_loopmgr only handles SIGTERM and SIGINT for application
  termination, but the application may install additional signal
  handlers, such as SIGHUP as a signal to reload configuration.

* new job running primitives, isc_job and isc_async, have been added.
  Both units schedule callbacks (specifying a callback function and
  argument) on an event loop. The difference is that isc_job unit is
  unlocked and not thread-safe, so it can be used to efficiently
  run jobs in the same thread, while isc_async is thread-safe and
  uses locking, so it can be used to pass jobs from one thread to
  another.

* isc_tid will be used to track the thread ID in isc_loop worker
  threads.

* unit tests have been added for the new APIs.
This commit is contained in:
Ondřej Surý
2022-07-26 13:03:22 +02:00
parent a26862e653
commit 84c90e223f
27 changed files with 2229 additions and 14 deletions

106
doc/dev/loopmgr.md Normal file
View File

@@ -0,0 +1,106 @@
<!--
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.

View File

@@ -9,6 +9,7 @@ libisc_la_HEADERS = \
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 \
@@ -42,10 +43,12 @@ libisc_la_HEADERS = \
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 \
@@ -77,6 +80,7 @@ libisc_la_HEADERS = \
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 \
@@ -90,6 +94,7 @@ libisc_la_HEADERS = \
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 \
@@ -98,7 +103,8 @@ libisc_la_HEADERS = \
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) \
@@ -114,6 +120,7 @@ libisc_la_SOURCES = \
app.c \
assertions.c \
astack.c \
async.c \
backtrace.c \
base32.c \
base64.c \
@@ -145,9 +152,13 @@ libisc_la_SOURCES = \
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 \
@@ -178,6 +189,7 @@ libisc_la_SOURCES = \
rwlock.c \
safe.c \
serial.c \
signal.c \
siphash.c \
sockaddr.c \
stats.c \
@@ -189,6 +201,7 @@ libisc_la_SOURCES = \
task.c \
task_p.h \
thread.c \
tid.c \
time.c \
timer.c \
timer_p.h \
@@ -199,7 +212,8 @@ libisc_la_SOURCES = \
trampoline_p.h \
url.c \
utf8.c \
uv.c
uv.c \
work.c
libisc_la_CPPFLAGS = \
$(AM_CPPFLAGS) \

57
lib/isc/async.c Normal file
View File

@@ -0,0 +1,57 @@
/*
* 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);
}

View File

@@ -0,0 +1,48 @@
/*
* 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

49
lib/isc/include/isc/job.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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

184
lib/isc/include/isc/loop.h Normal file
View File

@@ -0,0 +1,184 @@
/*
* 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

View File

@@ -149,4 +149,34 @@ isc_refcount_decrement(isc_refcount_t *target) {
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

View File

@@ -0,0 +1,55 @@
/*
* 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

35
lib/isc/include/isc/tid.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* 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

View File

@@ -55,16 +55,19 @@ typedef struct isc_httpdurl isc_httpdurl_t; /*%< HTTP URL */
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 */
@@ -72,6 +75,7 @@ typedef struct isc_quota isc_quota_t; /*%< Quota */
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
* */
@@ -85,6 +89,8 @@ typedef struct isc_textregion isc_textregion_t; /*%< Text Region */
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;

View File

@@ -17,6 +17,7 @@
#include <uv.h>
#include <isc/result.h>
#include <isc/tid.h>
/*
* These functions were introduced in newer libuv, but we still
@@ -128,3 +129,38 @@ isc__uverr2result(int uverr, bool dolog, const char *file, unsigned int line,
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

View File

@@ -0,0 +1,41 @@
/*
* 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

136
lib/isc/job.c Normal file
View File

@@ -0,0 +1,136 @@
/*
* 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);
}

26
lib/isc/job_p.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* 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);

592
lib/isc/loop.c Normal file
View File

@@ -0,0 +1,592 @@
/*
* 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);
}

149
lib/isc/loop_p.h Normal file
View File

@@ -0,0 +1,149 @@
/*
* 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);

89
lib/isc/signal.c Normal file
View File

@@ -0,0 +1,89 @@
/*
* 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);
}

48
lib/isc/tid.c Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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);
}

View File

@@ -15,12 +15,12 @@
#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"

68
lib/isc/work.c Normal file
View File

@@ -0,0 +1,68 @@
/*
* 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);
}

View File

@@ -44,6 +44,8 @@
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;
@@ -51,6 +53,16 @@ extern isc_task_t *maintask;
#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

View File

@@ -11,6 +11,7 @@ LDADD += \
check_PROGRAMS = \
aes_test \
async_test \
buffer_test \
counter_test \
crc64_test \
@@ -20,7 +21,9 @@ check_PROGRAMS = \
heap_test \
hmac_test \
ht_test \
job_test \
lex_test \
loop_test \
md_test \
mem_test \
netaddr_test \
@@ -38,7 +41,8 @@ check_PROGRAMS = \
symtab_test \
task_test \
time_test \
timer_test
timer_test \
work_test
if HAVE_LIBNGHTTP2
check_PROGRAMS += \

75
tests/isc/async_test.c Normal file
View File

@@ -0,0 +1,75 @@
/*
* 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

83
tests/isc/job_test.c Normal file
View File

@@ -0,0 +1,83 @@
/*
* 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

141
tests/isc/loop_test.c Normal file
View File

@@ -0,0 +1,141 @@
/*
* 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

81
tests/isc/work_test.c Normal file
View File

@@ -0,0 +1,81 @@
/*
* 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

View File

@@ -21,6 +21,7 @@
#include <isc/buffer.h>
#include <isc/hash.h>
#include <isc/loop.h>
#include <isc/managers.h>
#include <isc/mem.h>
#include <isc/os.h>
@@ -36,12 +37,61 @@
#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;