15185a700Sflorian /*
25185a700Sflorian * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian *
45185a700Sflorian * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian * copyright notice and this permission notice appear in all copies.
75185a700Sflorian *
85185a700Sflorian * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian */
165185a700Sflorian
175185a700Sflorian /*! \file */
185185a700Sflorian
195185a700Sflorian #include <stddef.h>
205185a700Sflorian #include <errno.h>
215185a700Sflorian #include <signal.h>
225185a700Sflorian #include <sys/time.h>
234465bcfbSjsg #include <time.h>
245185a700Sflorian
255185a700Sflorian #include <isc/app.h>
265185a700Sflorian #include <isc/event.h>
275185a700Sflorian
285185a700Sflorian #include <string.h>
295185a700Sflorian #include <isc/task.h>
305185a700Sflorian #include <isc/util.h>
315185a700Sflorian
325185a700Sflorian /*%
335185a700Sflorian * For BIND9 internal applications built with threads, we use a single app
345185a700Sflorian * context and let multiple worker, I/O, timer threads do actual jobs.
355185a700Sflorian * For other cases (including BIND9 built without threads) an app context acts
365185a700Sflorian * as an event loop dispatching various events.
375185a700Sflorian */
385185a700Sflorian #include "../timer_p.h"
395185a700Sflorian #include "../task_p.h"
405185a700Sflorian #include "socket_p.h"
415185a700Sflorian
425185a700Sflorian /*
435185a700Sflorian * The application context of this module. This implementation actually
445185a700Sflorian * doesn't use it. (This may change in the future).
455185a700Sflorian */
465185a700Sflorian
478b553854Sflorian typedef struct isc_appctx {
485185a700Sflorian isc_eventlist_t on_run;
49*1fb015a8Sflorian int shutdown_requested;
50*1fb015a8Sflorian int running;
515185a700Sflorian
525185a700Sflorian /*!
535185a700Sflorian * We assume that 'want_shutdown' can be read and written atomically.
545185a700Sflorian */
55*1fb015a8Sflorian int want_shutdown;
565185a700Sflorian
575185a700Sflorian isc_taskmgr_t *taskmgr;
585185a700Sflorian isc_socketmgr_t *socketmgr;
595185a700Sflorian isc_timermgr_t *timermgr;
608b553854Sflorian } isc_appctx_t;
615185a700Sflorian
628b553854Sflorian static isc_appctx_t isc_g_appctx;
638b553854Sflorian
648b553854Sflorian static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
658b553854Sflorian isc_taskaction_t action, void *arg);
665185a700Sflorian
678b553854Sflorian static isc_result_t
isc_app_ctxstart(isc_appctx_t * ctx)688b553854Sflorian isc_app_ctxstart(isc_appctx_t *ctx) {
695185a700Sflorian /*
705185a700Sflorian * Start an ISC library application.
715185a700Sflorian */
725185a700Sflorian
735185a700Sflorian ISC_LIST_INIT(ctx->on_run);
745185a700Sflorian
75*1fb015a8Sflorian ctx->shutdown_requested = 0;
76*1fb015a8Sflorian ctx->running = 0;
77*1fb015a8Sflorian ctx->want_shutdown = 0;
785185a700Sflorian
7976e675f5Sjung if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
805185a700Sflorian UNEXPECTED_ERROR(__FILE__, __LINE__,
8176e675f5Sjung "isc_app_ctxstart() signal: %s",
8240adc7c5Sjung strerror(errno));
8376e675f5Sjung return ISC_R_UNEXPECTED;
845185a700Sflorian }
8576e675f5Sjung return ISC_R_SUCCESS;
865185a700Sflorian }
875185a700Sflorian
885185a700Sflorian isc_result_t
isc_app_start(void)898b553854Sflorian isc_app_start(void) {
905185a700Sflorian /* The remaining members will be initialized in ctxstart() */
915185a700Sflorian
928b553854Sflorian return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx));
935185a700Sflorian }
945185a700Sflorian
955185a700Sflorian isc_result_t
isc_app_onrun(isc_task_t * task,isc_taskaction_t action,void * arg)968b553854Sflorian isc_app_onrun(isc_task_t *task, isc_taskaction_t action,
975185a700Sflorian void *arg)
985185a700Sflorian {
998b553854Sflorian return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx,
1005185a700Sflorian task, action, arg));
1015185a700Sflorian }
1025185a700Sflorian
1035185a700Sflorian isc_result_t
isc_app_ctxonrun(isc_appctx_t * ctx,isc_task_t * task,isc_taskaction_t action,void * arg)1048b553854Sflorian isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task,
1055185a700Sflorian isc_taskaction_t action, void *arg)
1065185a700Sflorian {
1075185a700Sflorian isc_event_t *event;
1085185a700Sflorian isc_task_t *cloned_task = NULL;
1095185a700Sflorian isc_result_t result;
1105185a700Sflorian
1115185a700Sflorian if (ctx->running) {
1125185a700Sflorian result = ISC_R_ALREADYRUNNING;
1135185a700Sflorian goto unlock;
1145185a700Sflorian }
1155185a700Sflorian
1165185a700Sflorian /*
1175185a700Sflorian * Note that we store the task to which we're going to send the event
1185185a700Sflorian * in the event's "sender" field.
1195185a700Sflorian */
1205185a700Sflorian isc_task_attach(task, &cloned_task);
1215185a700Sflorian event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN,
1225185a700Sflorian action, arg, sizeof(*event));
1235185a700Sflorian if (event == NULL) {
1245185a700Sflorian isc_task_detach(&cloned_task);
1255185a700Sflorian result = ISC_R_NOMEMORY;
1265185a700Sflorian goto unlock;
1275185a700Sflorian }
1285185a700Sflorian
1295185a700Sflorian ISC_LIST_APPEND(ctx->on_run, event, ev_link);
1305185a700Sflorian
1315185a700Sflorian result = ISC_R_SUCCESS;
1325185a700Sflorian
1335185a700Sflorian unlock:
1345185a700Sflorian return (result);
1355185a700Sflorian }
1365185a700Sflorian
1375185a700Sflorian /*!
1385185a700Sflorian * Event loop for nonthreaded programs.
1395185a700Sflorian */
1405185a700Sflorian static isc_result_t
evloop(isc_appctx_t * ctx)1418b553854Sflorian evloop(isc_appctx_t *ctx) {
1425185a700Sflorian isc_result_t result;
1435185a700Sflorian
1445185a700Sflorian while (!ctx->want_shutdown) {
1455185a700Sflorian int n;
146427f8978Sflorian struct timespec when, now, diff, zero ={0, 0};
1475185a700Sflorian struct timeval tv, *tvp;
1485185a700Sflorian isc_socketwait_t *swait;
149*1fb015a8Sflorian int readytasks;
150*1fb015a8Sflorian int call_timer_dispatch = 0;
1515185a700Sflorian
1528b553854Sflorian readytasks = isc_taskmgr_ready(ctx->taskmgr);
1535185a700Sflorian if (readytasks) {
1545185a700Sflorian tv.tv_sec = 0;
1555185a700Sflorian tv.tv_usec = 0;
1565185a700Sflorian tvp = &tv;
157*1fb015a8Sflorian call_timer_dispatch = 1;
1585185a700Sflorian } else {
1598b553854Sflorian result = isc_timermgr_nextevent(ctx->timermgr, &when);
1605185a700Sflorian if (result != ISC_R_SUCCESS)
1615185a700Sflorian tvp = NULL;
1625185a700Sflorian else {
163b53d8310Sflorian clock_gettime(CLOCK_MONOTONIC, &now);
164427f8978Sflorian timespecsub(&when, &now, &diff);
165427f8978Sflorian if (timespeccmp(&diff, &zero, <=)) {
166*1fb015a8Sflorian call_timer_dispatch = 1;
167427f8978Sflorian memset(&tv, 0, sizeof(tv));
168427f8978Sflorian } else
169427f8978Sflorian TIMESPEC_TO_TIMEVAL(&tv, &diff);
1705185a700Sflorian tvp = &tv;
1715185a700Sflorian }
1725185a700Sflorian }
1735185a700Sflorian
1745185a700Sflorian swait = NULL;
1758b553854Sflorian n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait);
1765185a700Sflorian
1775185a700Sflorian if (n == 0 || call_timer_dispatch) {
1785185a700Sflorian /*
1798b553854Sflorian * We call isc_timermgr_dispatch() only when
1805185a700Sflorian * necessary, in order to reduce overhead. If the
1815185a700Sflorian * select() call indicates a timeout, we need the
1825185a700Sflorian * dispatch. Even if not, if we set the 0-timeout
1835185a700Sflorian * for the select() call, we need to check the timer
1845185a700Sflorian * events. In the 'readytasks' case, there may be no
1855185a700Sflorian * timeout event actually, but there is no other way
1865185a700Sflorian * to reduce the overhead.
1875185a700Sflorian * Note that we do not have to worry about the case
1885185a700Sflorian * where a new timer is inserted during the select()
1895185a700Sflorian * call, since this loop only runs in the non-thread
1905185a700Sflorian * mode.
1915185a700Sflorian */
1928b553854Sflorian isc_timermgr_dispatch(ctx->timermgr);
1935185a700Sflorian }
1945185a700Sflorian if (n > 0)
1958b553854Sflorian (void)isc_socketmgr_dispatch(ctx->socketmgr, swait);
1968b553854Sflorian (void)isc_taskmgr_dispatch(ctx->taskmgr);
1975185a700Sflorian }
1985185a700Sflorian return (ISC_R_SUCCESS);
1995185a700Sflorian }
2005185a700Sflorian
2018b553854Sflorian static isc_result_t
isc_app_ctxrun(isc_appctx_t * ctx)2028b553854Sflorian isc_app_ctxrun(isc_appctx_t *ctx) {
2035185a700Sflorian int result;
2045185a700Sflorian isc_event_t *event, *next_event;
2055185a700Sflorian isc_task_t *task;
2065185a700Sflorian
2075185a700Sflorian if (!ctx->running) {
208*1fb015a8Sflorian ctx->running = 1;
2095185a700Sflorian
2105185a700Sflorian /*
2115185a700Sflorian * Post any on-run events (in FIFO order).
2125185a700Sflorian */
2135185a700Sflorian for (event = ISC_LIST_HEAD(ctx->on_run);
2145185a700Sflorian event != NULL;
2155185a700Sflorian event = next_event) {
2165185a700Sflorian next_event = ISC_LIST_NEXT(event, ev_link);
2175185a700Sflorian ISC_LIST_UNLINK(ctx->on_run, event, ev_link);
2185185a700Sflorian task = event->ev_sender;
2195185a700Sflorian event->ev_sender = NULL;
2205185a700Sflorian isc_task_sendanddetach(&task, &event);
2215185a700Sflorian }
2225185a700Sflorian
2235185a700Sflorian }
2245185a700Sflorian
2258b553854Sflorian (void) isc_taskmgr_dispatch(ctx->taskmgr);
2265185a700Sflorian result = evloop(ctx);
2275185a700Sflorian return (result);
2285185a700Sflorian }
2295185a700Sflorian
2305185a700Sflorian isc_result_t
isc_app_run(void)2318b553854Sflorian isc_app_run(void) {
2328b553854Sflorian return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx));
2335185a700Sflorian }
2345185a700Sflorian
2358b553854Sflorian static isc_result_t
isc_app_ctxshutdown(isc_appctx_t * ctx)2368b553854Sflorian isc_app_ctxshutdown(isc_appctx_t *ctx) {
237*1fb015a8Sflorian int want_kill = 1;
2385185a700Sflorian
2395185a700Sflorian REQUIRE(ctx->running);
2405185a700Sflorian
2415185a700Sflorian if (ctx->shutdown_requested)
242*1fb015a8Sflorian want_kill = 0;
2435185a700Sflorian else
244*1fb015a8Sflorian ctx->shutdown_requested = 1;
2455185a700Sflorian
2465185a700Sflorian if (want_kill) {
2475185a700Sflorian if (ctx != &isc_g_appctx)
2485185a700Sflorian /* BIND9 internal, but using multiple contexts */
249*1fb015a8Sflorian ctx->want_shutdown = 1;
2505185a700Sflorian else {
251*1fb015a8Sflorian ctx->want_shutdown = 1;
2525185a700Sflorian }
2535185a700Sflorian }
2545185a700Sflorian
2555185a700Sflorian return (ISC_R_SUCCESS);
2565185a700Sflorian }
2575185a700Sflorian
2585185a700Sflorian isc_result_t
isc_app_shutdown(void)2598b553854Sflorian isc_app_shutdown(void) {
2608b553854Sflorian return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx));
2615185a700Sflorian }
262