#include <stddef.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <isc/app.h>
#include <isc/event.h>
#include <string.h>
#include <isc/task.h>
#include <isc/util.h>
#include "../timer_p.h"
#include "../task_p.h"
#include "socket_p.h"
typedef struct isc_appctx {
isc_eventlist_t on_run;
int shutdown_requested;
int running;
int want_shutdown;
isc_taskmgr_t *taskmgr;
isc_socketmgr_t *socketmgr;
isc_timermgr_t *timermgr;
} isc_appctx_t;
static isc_appctx_t isc_g_appctx;
static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
isc_taskaction_t action, void *arg);
static isc_result_t
isc_app_ctxstart(isc_appctx_t *ctx) {
ISC_LIST_INIT(ctx->on_run);
ctx->shutdown_requested = 0;
ctx->running = 0;
ctx->want_shutdown = 0;
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_app_ctxstart() signal: %s",
strerror(errno));
return ISC_R_UNEXPECTED;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_app_start(void) {
return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx));
}
isc_result_t
isc_app_onrun(isc_task_t *task, isc_taskaction_t action,
void *arg)
{
return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx,
task, action, arg));
}
isc_result_t
isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
isc_taskaction_t action, void *arg)
{
isc_event_t *event;
isc_task_t *cloned_task = NULL;
isc_result_t result;
if (ctx->running) {
result = ISC_R_ALREADYRUNNING;
goto unlock;
}
isc_task_attach(task, &cloned_task);
event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN,
action, arg, sizeof(*event));
if (event == NULL) {
isc_task_detach(&cloned_task);
result = ISC_R_NOMEMORY;
goto unlock;
}
ISC_LIST_APPEND(ctx->on_run, event, ev_link);
result = ISC_R_SUCCESS;
unlock:
return (result);
}
static isc_result_t
evloop(isc_appctx_t *ctx) {
isc_result_t result;
while (!ctx->want_shutdown) {
int n;
struct timespec when, now, diff, zero ={0, 0};
struct timeval tv, *tvp;
isc_socketwait_t *swait;
int readytasks;
int call_timer_dispatch = 0;
readytasks = isc_taskmgr_ready(ctx->taskmgr);
if (readytasks) {
tv.tv_sec = 0;
tv.tv_usec = 0;
tvp = &tv;
call_timer_dispatch = 1;
} else {
result = isc_timermgr_nextevent(ctx->timermgr, &when);
if (result != ISC_R_SUCCESS)
tvp = NULL;
else {
clock_gettime(CLOCK_MONOTONIC, &now);
timespecsub(&when, &now, &diff);
if (timespeccmp(&diff, &zero, <=)) {
call_timer_dispatch = 1;
memset(&tv, 0, sizeof(tv));
} else
TIMESPEC_TO_TIMEVAL(&tv, &diff);
tvp = &tv;
}
}
swait = NULL;
n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait);
if (n == 0 || call_timer_dispatch) {
isc_timermgr_dispatch(ctx->timermgr);
}
if (n > 0)
(void)isc_socketmgr_dispatch(ctx->socketmgr, swait);
(void)isc_taskmgr_dispatch(ctx->taskmgr);
}
return (ISC_R_SUCCESS);
}
static isc_result_t
isc_app_ctxrun(isc_appctx_t *ctx) {
int result;
isc_event_t *event, *next_event;
isc_task_t *task;
if (!ctx->running) {
ctx->running = 1;
for (event = ISC_LIST_HEAD(ctx->on_run);
event != NULL;
event = next_event) {
next_event = ISC_LIST_NEXT(event, ev_link);
ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
task = event->ev_sender;
event->ev_sender = NULL;
isc_task_sendanddetach(&task, &event);
}
}
(void) isc_taskmgr_dispatch(ctx->taskmgr);
result = evloop(ctx);
return (result);
}
isc_result_t
isc_app_run(void) {
return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx));
}
static isc_result_t
isc_app_ctxshutdown(isc_appctx_t *ctx) {
int want_kill = 1;
REQUIRE(ctx->running);
if (ctx->shutdown_requested)
want_kill = 0;
else
ctx->shutdown_requested = 1;
if (want_kill) {
if (ctx != &isc_g_appctx)
ctx->want_shutdown = 1;
else {
ctx->want_shutdown = 1;
}
}
return (ISC_R_SUCCESS);
}
isc_result_t
isc_app_shutdown(void) {
return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx));
}