WIP: Add loopmgr that replaces isc_app, and parts of isc_nm related to the uv_loops
Now includes <isc/signal.h>
This commit is contained in:
@@ -46,6 +46,7 @@ libisc_la_HEADERS = \
|
||||
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 \
|
||||
@@ -78,6 +79,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 \
|
||||
@@ -151,6 +153,7 @@ libisc_la_SOURCES = \
|
||||
lex.c \
|
||||
lib.c \
|
||||
log.c \
|
||||
loop.c \
|
||||
managers.c \
|
||||
md.c \
|
||||
mem.c \
|
||||
@@ -181,6 +184,7 @@ libisc_la_SOURCES = \
|
||||
rwlock.c \
|
||||
safe.c \
|
||||
serial.c \
|
||||
signal.c \
|
||||
siphash.c \
|
||||
sockaddr.c \
|
||||
stats.c \
|
||||
|
||||
165
lib/isc/include/isc/loop.h
Normal file
165
lib/isc/include/isc/loop.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 <uv.h>
|
||||
|
||||
#include <isc/barrier.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/refcount.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/thread.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
typedef struct isc_loopmgr isc_loopmgr_t;
|
||||
typedef struct isc_loop isc_loop_t;
|
||||
typedef struct isc_job isc_job_t;
|
||||
|
||||
typedef void (*isc_job_cb)(void *);
|
||||
|
||||
typedef struct isc_signal isc_signal_t;
|
||||
|
||||
typedef void (*isc_signal_cb)(void *, int);
|
||||
|
||||
#define ISC_LOOPMGR_TID_UNKNOWN UINT32_MAX
|
||||
|
||||
struct isc_signal {
|
||||
uv_signal_t signal;
|
||||
isc_mem_t *mctx;
|
||||
isc_signal_cb cb;
|
||||
void *cbarg;
|
||||
int signum;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-thread loop
|
||||
*/
|
||||
#define LOOP_MAGIC ISC_MAGIC('L', 'O', 'O', 'P')
|
||||
#define VALID_LOOP(t) ISC_MAGIC_VALID(t, LOOP_MAGIC)
|
||||
|
||||
struct isc_job {
|
||||
isc_mem_t *mctx;
|
||||
uv_idle_t idle;
|
||||
isc_job_cb cb;
|
||||
void *cbarg;
|
||||
LINK(isc_job_t) link;
|
||||
};
|
||||
|
||||
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;
|
||||
bool finished;
|
||||
bool shuttingdown;
|
||||
|
||||
/* Pause */
|
||||
uv_async_t pause;
|
||||
|
||||
/* Shutdown */
|
||||
uv_async_t shutdown;
|
||||
ISC_LIST(isc_job_t) ctors;
|
||||
ISC_LIST(isc_job_t) dtors;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_refcount_t references;
|
||||
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;
|
||||
|
||||
/* per-thread objects */
|
||||
isc_loop_t *loops;
|
||||
};
|
||||
|
||||
/* FIXME: Deduplicate with netmgr-int.h */
|
||||
#define UV_RUNTIME_CHECK(func, ret) \
|
||||
if (ret != 0) { \
|
||||
isc_error_fatal(__FILE__, __LINE__, "%s failed: %s\n", #func, \
|
||||
uv_strerror(ret)); \
|
||||
}
|
||||
|
||||
#define DEFAULT_LOOP(loopmgr) (&(loopmgr)->loops[0])
|
||||
#define CURRENT_LOOP(loopmgr) (&(loopmgr)->loops[isc__loopmgr_tid_v])
|
||||
|
||||
isc_loopmgr_t *
|
||||
isc_loopmgr_new(isc_mem_t *, uint32_t);
|
||||
|
||||
void
|
||||
isc_loopmgr_destroy(isc_loopmgr_t **);
|
||||
|
||||
void
|
||||
isc_loopmgr_shutdown(isc_loopmgr_t *);
|
||||
|
||||
isc_loop_t *
|
||||
isc_loopmgr_getloop(isc_loopmgr_t *);
|
||||
|
||||
int
|
||||
isc_loopmgr_tid(void);
|
||||
|
||||
void
|
||||
isc_loopmgr_run(isc_loopmgr_t *);
|
||||
|
||||
void
|
||||
isc_loopmgr_pause(isc_loopmgr_t *);
|
||||
|
||||
void
|
||||
isc_loopmgr_resume(isc_loopmgr_t *);
|
||||
|
||||
void
|
||||
isc_loopmgr_schedule_ctor(isc_loopmgr_t *, isc_job_cb, void *);
|
||||
|
||||
void
|
||||
isc_loopmgr_schedule_dtor(isc_loopmgr_t *, isc_job_cb, void *);
|
||||
|
||||
void
|
||||
isc_loop_schedule_ctor(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc_loop_schedule_dtor(isc_loop_t *loop, isc_job_cb cb, void *cbarg);
|
||||
|
||||
void
|
||||
isc_loop_mem_attach(isc_loop_t *loop, isc_mem_t **mctx);
|
||||
|
||||
isc_loop_t *
|
||||
isc_loopmgr_default_loop(isc_loopmgr_t *loopmgr);
|
||||
30
lib/isc/include/isc/signal.h
Normal file
30
lib/isc/include/isc/signal.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 <uv.h>
|
||||
|
||||
#include <isc/loop.h>
|
||||
|
||||
isc_signal_t *
|
||||
isc_signal_new(isc_loopmgr_t *loopmgr, isc_signal_cb cb, void *cbarg,
|
||||
int signum);
|
||||
|
||||
void
|
||||
isc_signal_free(isc_signal_t *signal);
|
||||
|
||||
void
|
||||
isc_signal_start(isc_signal_t *signal);
|
||||
|
||||
void
|
||||
isc_signal_stop(isc_signal_t *signal);
|
||||
467
lib/isc/loop.c
Normal file
467
lib/isc/loop.c
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
* 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 <uv.h>
|
||||
|
||||
#include <isc/atomic.h>
|
||||
#include <isc/barrier.h>
|
||||
#include <isc/condition.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>
|
||||
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
|
||||
static thread_local uint32_t isc__loopmgr_tid_v = ISC_LOOPMGR_TID_UNKNOWN;
|
||||
|
||||
static void
|
||||
ignore_signal(int sig, void (*handler)(int)) {
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.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);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isc__loopmgr_shutdown(isc_loopmgr_t *loopmgr) {
|
||||
REQUIRE(DEFAULT_LOOP(loopmgr) == CURRENT_LOOP(loopmgr));
|
||||
|
||||
if (!atomic_compare_exchange_strong(&loopmgr->shuttingdown,
|
||||
&(bool){ false }, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop the signal handlers */
|
||||
isc_signal_stop(loopmgr->sigterm);
|
||||
isc_signal_stop(loopmgr->sigint);
|
||||
|
||||
/* Free the signal handlers */
|
||||
isc_signal_free(loopmgr->sigterm);
|
||||
isc_signal_free(loopmgr->sigint);
|
||||
|
||||
for (size_t i = 0; i < loopmgr->nloops; i++) {
|
||||
isc_loop_t *loop = &loopmgr->loops[i];
|
||||
|
||||
uv_async_send(&loop->shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_loopmgr_shutdown(isc_loopmgr_t *loopmgr) {
|
||||
/* Invoked from non-default loop, just pass the signal */
|
||||
if (DEFAULT_LOOP(loopmgr) != CURRENT_LOOP(loopmgr)) {
|
||||
kill(getpid(), SIGTERM);
|
||||
return;
|
||||
}
|
||||
|
||||
isc__loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
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 =
|
||||
(isc_loop_t *)uv_handle_get_data((uv_handle_t *)handle);
|
||||
|
||||
pause_loop(loop);
|
||||
resume_loop(loop);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__job_free(uv_handle_t *handle) {
|
||||
isc_job_t *job = uv_handle_get_data(handle);
|
||||
|
||||
isc_mem_putanddetach(&job->mctx, job, sizeof(*job));
|
||||
}
|
||||
|
||||
static void
|
||||
isc__job_cb(uv_idle_t *idle) {
|
||||
isc_job_t *job = uv_handle_get_data((uv_handle_t *)idle);
|
||||
|
||||
job->cb(job->cbarg);
|
||||
|
||||
uv_idle_stop(idle);
|
||||
uv_close((uv_handle_t *)idle, isc__job_free);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_cb(uv_async_t *handle) {
|
||||
isc_job_t *job = NULL;
|
||||
isc_loop_t *loop =
|
||||
(isc_loop_t *)uv_handle_get_data((uv_handle_t *)handle);
|
||||
|
||||
/*
|
||||
* The loop resources are freed only after uv_run() is finished, so we
|
||||
* don't need to worry about freeing memory used for async callbacks.
|
||||
*/
|
||||
uv_close((uv_handle_t *)&loop->shutdown, NULL);
|
||||
uv_close((uv_handle_t *)&loop->pause, NULL);
|
||||
|
||||
job = ISC_LIST_HEAD(loop->dtors);
|
||||
while (job != NULL) {
|
||||
int r;
|
||||
isc_job_t *next = ISC_LIST_NEXT(job, link);
|
||||
ISC_LIST_UNLINK(loop->dtors, job, link);
|
||||
|
||||
r = uv_idle_start(&job->idle, isc__job_cb);
|
||||
UV_RUNTIME_CHECK(uv_idle_start, r);
|
||||
|
||||
job = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
loop_init(isc_loop_t *loop) {
|
||||
int r = uv_loop_init(&loop->loop);
|
||||
UV_RUNTIME_CHECK(uv_loop_init, r);
|
||||
|
||||
r = uv_async_init(&loop->loop, &loop->pause, pauseresume_cb);
|
||||
UV_RUNTIME_CHECK(uv_async_init, r);
|
||||
uv_handle_set_data((uv_handle_t *)&loop->pause, loop);
|
||||
|
||||
r = uv_async_init(&loop->loop, &loop->shutdown, shutdown_cb);
|
||||
UV_RUNTIME_CHECK(uv_async_init, r);
|
||||
uv_handle_set_data((uv_handle_t *)&loop->shutdown, loop);
|
||||
|
||||
isc_mem_create(&loop->mctx);
|
||||
|
||||
ISC_LIST_INIT(loop->ctors);
|
||||
ISC_LIST_INIT(loop->dtors);
|
||||
}
|
||||
|
||||
static void
|
||||
loop_run(isc_loop_t *loop) {
|
||||
int r;
|
||||
isc_job_t *job;
|
||||
|
||||
job = ISC_LIST_HEAD(loop->ctors);
|
||||
while (job != NULL) {
|
||||
isc_job_t *next = ISC_LIST_NEXT(job, link);
|
||||
ISC_LIST_UNLINK(loop->ctors, job, link);
|
||||
|
||||
r = uv_idle_start(&job->idle, isc__job_cb);
|
||||
UV_RUNTIME_CHECK(uv_idle_start, r);
|
||||
|
||||
job = next;
|
||||
}
|
||||
|
||||
r = uv_run(&loop->loop, UV_RUN_DEFAULT);
|
||||
UV_RUNTIME_CHECK(uv_run, r);
|
||||
}
|
||||
|
||||
static void
|
||||
loop_close(isc_loop_t *loop) {
|
||||
int r = uv_loop_close(&loop->loop);
|
||||
UV_RUNTIME_CHECK(uv_loop_close, r);
|
||||
|
||||
isc_mem_detach(&loop->mctx);
|
||||
}
|
||||
|
||||
static isc_threadresult_t
|
||||
loop_thread(isc_threadarg_t arg) {
|
||||
isc_loop_t *loop = (isc_loop_t *)arg;
|
||||
|
||||
REQUIRE(VALID_LOOP(loop));
|
||||
|
||||
/* Initialize the thread_local variable */
|
||||
isc__loopmgr_tid_v = loop->tid;
|
||||
|
||||
loop_run(loop);
|
||||
|
||||
return ((isc_threadresult_t)0);
|
||||
}
|
||||
|
||||
enum {
|
||||
isc_loop_ctor,
|
||||
isc_loop_dtor,
|
||||
};
|
||||
|
||||
static void
|
||||
isc__loop_schedule(isc_loop_t *loop, int when, isc_job_cb cb, void *cbarg) {
|
||||
isc_job_t *job = NULL;
|
||||
isc_loopmgr_t *loopmgr;
|
||||
|
||||
REQUIRE(VALID_LOOP(loop));
|
||||
|
||||
loopmgr = loop->loopmgr;
|
||||
|
||||
REQUIRE(loop->tid == isc__loopmgr_tid_v ||
|
||||
!atomic_load(&loopmgr->running) ||
|
||||
atomic_load(&loopmgr->paused));
|
||||
|
||||
job = isc_mem_get(loop->mctx, sizeof(*job));
|
||||
*job = (isc_job_t){
|
||||
.cb = cb,
|
||||
.cbarg = cbarg,
|
||||
};
|
||||
|
||||
ISC_LINK_INIT(job, link);
|
||||
|
||||
isc_mem_attach(loop->mctx, &job->mctx);
|
||||
|
||||
int r = uv_idle_init(&loop->loop, &job->idle);
|
||||
UV_RUNTIME_CHECK(uv_idle_init, r);
|
||||
uv_handle_set_data((uv_handle_t *)&job->idle, job);
|
||||
|
||||
switch (when) {
|
||||
case isc_loop_ctor:
|
||||
ISC_LIST_PREPEND(loop->ctors, job, link);
|
||||
break;
|
||||
case isc_loop_dtor:
|
||||
ISC_LIST_PREPEND(loop->dtors, job, link);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public
|
||||
*/
|
||||
|
||||
int
|
||||
isc_loopmgr_tid(void) {
|
||||
return (isc__loopmgr_tid_v);
|
||||
}
|
||||
|
||||
isc_loopmgr_t *
|
||||
isc_loopmgr_new(isc_mem_t *mctx, uint32_t nloops) {
|
||||
isc_loopmgr_t *loopmgr = NULL;
|
||||
|
||||
REQUIRE(nloops > 0);
|
||||
|
||||
loopmgr = isc_mem_get(mctx, sizeof(*loopmgr));
|
||||
*loopmgr = (isc_loopmgr_t){
|
||||
.nloops = nloops,
|
||||
};
|
||||
|
||||
isc_mem_attach(mctx, &loopmgr->mctx);
|
||||
isc_refcount_init(&loopmgr->references, 1);
|
||||
|
||||
isc_barrier_init(&loopmgr->pausing, loopmgr->nloops);
|
||||
isc_barrier_init(&loopmgr->resuming, 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 = (isc_loop_t){
|
||||
.tid = i,
|
||||
.loopmgr = loopmgr,
|
||||
.magic = LOOP_MAGIC,
|
||||
};
|
||||
|
||||
loop_init(loop);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return (loopmgr);
|
||||
}
|
||||
|
||||
void
|
||||
isc_loop_schedule_ctor(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
|
||||
isc__loop_schedule(loop, isc_loop_ctor, cb, cbarg);
|
||||
}
|
||||
|
||||
void
|
||||
isc_loop_schedule_dtor(isc_loop_t *loop, isc_job_cb cb, void *cbarg) {
|
||||
isc__loop_schedule(loop, isc_loop_dtor, cb, cbarg);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__loopmgr_schedule(isc_loopmgr_t *loopmgr, int when, 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];
|
||||
isc__loop_schedule(loop, when, cb, cbarg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_loopmgr_schedule_ctor(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
|
||||
isc__loopmgr_schedule(loopmgr, isc_loop_ctor, cb, cbarg);
|
||||
}
|
||||
|
||||
void
|
||||
isc_loopmgr_schedule_dtor(isc_loopmgr_t *loopmgr, isc_job_cb cb, void *cbarg) {
|
||||
isc__loopmgr_schedule(loopmgr, isc_loop_dtor, 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++) {
|
||||
isc_loop_t *loop = &loopmgr->loops[i];
|
||||
|
||||
isc_thread_create(loop_thread, loop, &loop->thread);
|
||||
}
|
||||
|
||||
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__loopmgr_tid_v) {
|
||||
continue;
|
||||
}
|
||||
uv_async_send(&loop->pause);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
isc_refcount_decrement0(&loopmgr->references);
|
||||
isc_refcount_destroy(&loopmgr->references);
|
||||
|
||||
loopmgr->magic = 0;
|
||||
|
||||
/* FIXME: We need to split the ->running and ->paused variable */
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < loopmgr->nloops; i++) {
|
||||
isc_loop_t *loop = &loopmgr->loops[i];
|
||||
|
||||
loop_close(loop);
|
||||
loop->magic = 0;
|
||||
}
|
||||
isc_mem_put(loopmgr->mctx, loopmgr->loops,
|
||||
loopmgr->nloops * sizeof(loopmgr->loops[0]));
|
||||
|
||||
isc_barrier_destroy(&loopmgr->resuming);
|
||||
isc_barrier_destroy(&loopmgr->pausing);
|
||||
|
||||
isc_mem_putanddetach(&loopmgr->mctx, loopmgr, sizeof(*loopmgr));
|
||||
}
|
||||
|
||||
void
|
||||
isc_loop_mem_attach(isc_loop_t *loop, isc_mem_t **mctxp) {
|
||||
REQUIRE(VALID_LOOP(loop));
|
||||
REQUIRE(mctxp != NULL && *mctxp == NULL);
|
||||
|
||||
isc_mem_attach(loop->mctx, mctxp);
|
||||
}
|
||||
|
||||
isc_loop_t *
|
||||
isc_loopmgr_default_loop(isc_loopmgr_t *loopmgr) {
|
||||
REQUIRE(VALID_LOOPMGR(loopmgr));
|
||||
|
||||
return (DEFAULT_LOOP(loopmgr));
|
||||
}
|
||||
79
lib/isc/signal.c
Normal file
79
lib/isc/signal.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 <uv.h>
|
||||
|
||||
#include <isc/loop.h>
|
||||
#include <isc/signal.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;
|
||||
isc_mem_t *mctx = NULL;
|
||||
|
||||
loop = DEFAULT_LOOP(loopmgr);
|
||||
|
||||
isc_loop_mem_attach(loop, &mctx);
|
||||
|
||||
signal = isc_mem_get(mctx, sizeof(*signal));
|
||||
*signal = (isc_signal_t){
|
||||
.mctx = mctx,
|
||||
.cb = cb,
|
||||
.cbarg = cbarg,
|
||||
.signum = signum,
|
||||
};
|
||||
|
||||
int 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_free(uv_handle_t *handle) {
|
||||
isc_signal_t *signal = uv_handle_get_data(handle);
|
||||
|
||||
isc_mem_putanddetach(&signal->mctx, signal, sizeof(*signal));
|
||||
}
|
||||
|
||||
void
|
||||
isc_signal_free(isc_signal_t *signal) {
|
||||
uv_close((uv_handle_t *)&signal->signal, isc__signal_free);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -25,6 +25,7 @@ check_PROGRAMS = \
|
||||
hmac_test \
|
||||
ht_test \
|
||||
lex_test \
|
||||
loop_test \
|
||||
md_test \
|
||||
mem_test \
|
||||
netaddr_test \
|
||||
@@ -70,6 +71,14 @@ hmac_test_LDADD = \
|
||||
$(LDADD) \
|
||||
$(OPENSSL_LIBS)
|
||||
|
||||
loop_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LIBUV_CFLAGS)
|
||||
|
||||
loop_test_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LIBUV_LIBS)
|
||||
|
||||
md_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(OPENSSL_CFLAGS)
|
||||
|
||||
203
lib/isc/tests/loop_test.c
Normal file
203
lib/isc/tests/loop_test.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if HAVE_CMOCKA
|
||||
|
||||
#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 "isctest.h"
|
||||
|
||||
static int
|
||||
setup_mctx(void **state) {
|
||||
UNUSED(state);
|
||||
|
||||
isc_mem_create(&test_mctx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
teardown_mctx(void **state) {
|
||||
UNUSED(state);
|
||||
|
||||
isc_mem_destroy(&test_mctx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
setup_loopmgr(void **state) {
|
||||
isc_loopmgr_t *loopmgr = NULL;
|
||||
|
||||
loopmgr = isc_loopmgr_new(test_mctx, isc_os_ncpus());
|
||||
if (loopmgr == NULL) {
|
||||
return (1);
|
||||
}
|
||||
|
||||
*state = loopmgr;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
teardown_loopmgr(void **state) {
|
||||
isc_loopmgr_t *loopmgr = *state;
|
||||
|
||||
isc_loopmgr_destroy(&loopmgr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static atomic_uint scheduled = 0;
|
||||
|
||||
static void
|
||||
count(void *arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
atomic_fetch_add(&scheduled, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_loopmgr(void *arg) {
|
||||
isc_loopmgr_t *loopmgr = (isc_loopmgr_t *)arg;
|
||||
|
||||
while (atomic_load(&scheduled) != loopmgr->nloops) {
|
||||
isc_thread_yield();
|
||||
}
|
||||
|
||||
isc_loopmgr_shutdown(loopmgr);
|
||||
}
|
||||
|
||||
static void
|
||||
isc_loopmgr_test(void **state) {
|
||||
isc_loopmgr_t *loopmgr = *state;
|
||||
|
||||
isc_loopmgr_schedule_ctor(loopmgr, count, loopmgr);
|
||||
|
||||
isc_loop_schedule_ctor(DEFAULT_LOOP(loopmgr), shutdown_loopmgr,
|
||||
loopmgr);
|
||||
|
||||
isc_loopmgr_run(loopmgr);
|
||||
|
||||
assert_int_equal(atomic_load(&scheduled), loopmgr->nloops);
|
||||
}
|
||||
|
||||
static void
|
||||
pause_loopmgr(void *arg) {
|
||||
isc_loopmgr_t *loopmgr = (isc_loopmgr_t *)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);
|
||||
}
|
||||
|
||||
static void
|
||||
isc_loopmgr_pause_test(void **state) {
|
||||
isc_loopmgr_t *loopmgr = *state;
|
||||
|
||||
isc_loop_schedule_ctor(DEFAULT_LOOP(loopmgr), pause_loopmgr, loopmgr);
|
||||
isc_loop_schedule_ctor(DEFAULT_LOOP(loopmgr), shutdown_loopmgr,
|
||||
loopmgr);
|
||||
|
||||
isc_loopmgr_run(loopmgr);
|
||||
}
|
||||
|
||||
static void
|
||||
send_sigint(void *arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
kill(getpid(), SIGINT);
|
||||
}
|
||||
|
||||
static void
|
||||
isc_loopmgr_sigint_test(void **state) {
|
||||
isc_loopmgr_t *loopmgr = *state;
|
||||
|
||||
isc_loop_schedule_ctor(CURRENT_LOOP(loopmgr), send_sigint, loopmgr);
|
||||
|
||||
isc_loopmgr_run(loopmgr);
|
||||
}
|
||||
|
||||
static void
|
||||
send_sigterm(void *arg) {
|
||||
UNUSED(arg);
|
||||
|
||||
kill(getpid(), SIGINT);
|
||||
}
|
||||
|
||||
static void
|
||||
isc_loopmgr_sigterm_test(void **state) {
|
||||
isc_loopmgr_t *loopmgr = *state;
|
||||
|
||||
isc_loop_schedule_ctor(CURRENT_LOOP(loopmgr), send_sigterm, loopmgr);
|
||||
|
||||
isc_loopmgr_run(loopmgr);
|
||||
}
|
||||
|
||||
int
|
||||
main(void) {
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(isc_loopmgr_test, setup_loopmgr,
|
||||
teardown_loopmgr),
|
||||
cmocka_unit_test_setup_teardown(isc_loopmgr_pause_test,
|
||||
setup_loopmgr,
|
||||
teardown_loopmgr),
|
||||
cmocka_unit_test_setup_teardown(isc_loopmgr_sigint_test,
|
||||
setup_loopmgr,
|
||||
teardown_loopmgr),
|
||||
cmocka_unit_test_setup_teardown(isc_loopmgr_sigterm_test,
|
||||
setup_loopmgr,
|
||||
teardown_loopmgr),
|
||||
};
|
||||
|
||||
return (cmocka_run_group_tests(tests, setup_mctx, teardown_mctx));
|
||||
}
|
||||
|
||||
#else /* HAVE_CMOCKA */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
printf("1..0 # Skipped: cmocka not available\n");
|
||||
return (SKIPPED_TEST_EXIT_CODE);
|
||||
}
|
||||
|
||||
#endif /* if HAVE_CMOCKA */
|
||||
Reference in New Issue
Block a user