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:
Ondřej Surý
2022-03-27 11:20:44 +02:00
committed by Evan Hunt
parent 3dd8af9aa8
commit 44a99d0158
7 changed files with 957 additions and 0 deletions

View File

@@ -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
View 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);

View 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
View 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
View 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);
}

View File

@@ -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
View 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 */