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:
106
doc/dev/loopmgr.md
Normal file
106
doc/dev/loopmgr.md
Normal 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.
|
||||
@@ -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
57
lib/isc/async.c
Normal 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);
|
||||
}
|
||||
48
lib/isc/include/isc/async.h
Normal file
48
lib/isc/include/isc/async.h
Normal 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
49
lib/isc/include/isc/job.h
Normal 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
184
lib/isc/include/isc/loop.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
55
lib/isc/include/isc/signal.h
Normal file
55
lib/isc/include/isc/signal.h
Normal 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
35
lib/isc/include/isc/tid.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
41
lib/isc/include/isc/work.h
Normal file
41
lib/isc/include/isc/work.h
Normal 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
136
lib/isc/job.c
Normal 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
26
lib/isc/job_p.h
Normal 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
592
lib/isc/loop.c
Normal 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
149
lib/isc/loop_p.h
Normal 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
89
lib/isc/signal.c
Normal 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
48
lib/isc/tid.c
Normal 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);
|
||||
}
|
||||
@@ -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
68
lib/isc/work.c
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
75
tests/isc/async_test.c
Normal 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
83
tests/isc/job_test.c
Normal 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
141
tests/isc/loop_test.c
Normal 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
81
tests/isc/work_test.c
Normal 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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user