1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /*! \file */ 18 19 #include <stddef.h> 20 #include <errno.h> 21 #include <signal.h> 22 #include <sys/time.h> 23 #include <time.h> 24 25 #include <isc/app.h> 26 #include <isc/event.h> 27 28 #include <string.h> 29 #include <isc/task.h> 30 #include <isc/util.h> 31 32 /*% 33 * For BIND9 internal applications built with threads, we use a single app 34 * context and let multiple worker, I/O, timer threads do actual jobs. 35 * For other cases (including BIND9 built without threads) an app context acts 36 * as an event loop dispatching various events. 37 */ 38 #include "../timer_p.h" 39 #include "../task_p.h" 40 #include "socket_p.h" 41 42 /* 43 * The application context of this module. This implementation actually 44 * doesn't use it. (This may change in the future). 45 */ 46 47 typedef struct isc_appctx { 48 isc_eventlist_t on_run; 49 int shutdown_requested; 50 int running; 51 52 /*! 53 * We assume that 'want_shutdown' can be read and written atomically. 54 */ 55 int want_shutdown; 56 57 isc_taskmgr_t *taskmgr; 58 isc_socketmgr_t *socketmgr; 59 isc_timermgr_t *timermgr; 60 } isc_appctx_t; 61 62 static isc_appctx_t isc_g_appctx; 63 64 static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task, 65 isc_taskaction_t action, void *arg); 66 67 static isc_result_t 68 isc_app_ctxstart(isc_appctx_t *ctx) { 69 /* 70 * Start an ISC library application. 71 */ 72 73 ISC_LIST_INIT(ctx->on_run); 74 75 ctx->shutdown_requested = 0; 76 ctx->running = 0; 77 ctx->want_shutdown = 0; 78 79 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { 80 UNEXPECTED_ERROR(__FILE__, __LINE__, 81 "isc_app_ctxstart() signal: %s", 82 strerror(errno)); 83 return ISC_R_UNEXPECTED; 84 } 85 return ISC_R_SUCCESS; 86 } 87 88 isc_result_t 89 isc_app_start(void) { 90 /* The remaining members will be initialized in ctxstart() */ 91 92 return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx)); 93 } 94 95 isc_result_t 96 isc_app_onrun(isc_task_t *task, isc_taskaction_t action, 97 void *arg) 98 { 99 return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx, 100 task, action, arg)); 101 } 102 103 isc_result_t 104 isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task, 105 isc_taskaction_t action, void *arg) 106 { 107 isc_event_t *event; 108 isc_task_t *cloned_task = NULL; 109 isc_result_t result; 110 111 if (ctx->running) { 112 result = ISC_R_ALREADYRUNNING; 113 goto unlock; 114 } 115 116 /* 117 * Note that we store the task to which we're going to send the event 118 * in the event's "sender" field. 119 */ 120 isc_task_attach(task, &cloned_task); 121 event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN, 122 action, arg, sizeof(*event)); 123 if (event == NULL) { 124 isc_task_detach(&cloned_task); 125 result = ISC_R_NOMEMORY; 126 goto unlock; 127 } 128 129 ISC_LIST_APPEND(ctx->on_run, event, ev_link); 130 131 result = ISC_R_SUCCESS; 132 133 unlock: 134 return (result); 135 } 136 137 /*! 138 * Event loop for nonthreaded programs. 139 */ 140 static isc_result_t 141 evloop(isc_appctx_t *ctx) { 142 isc_result_t result; 143 144 while (!ctx->want_shutdown) { 145 int n; 146 struct timespec when, now, diff, zero ={0, 0}; 147 struct timeval tv, *tvp; 148 isc_socketwait_t *swait; 149 int readytasks; 150 int call_timer_dispatch = 0; 151 152 readytasks = isc_taskmgr_ready(ctx->taskmgr); 153 if (readytasks) { 154 tv.tv_sec = 0; 155 tv.tv_usec = 0; 156 tvp = &tv; 157 call_timer_dispatch = 1; 158 } else { 159 result = isc_timermgr_nextevent(ctx->timermgr, &when); 160 if (result != ISC_R_SUCCESS) 161 tvp = NULL; 162 else { 163 clock_gettime(CLOCK_MONOTONIC, &now); 164 timespecsub(&when, &now, &diff); 165 if (timespeccmp(&diff, &zero, <=)) { 166 call_timer_dispatch = 1; 167 memset(&tv, 0, sizeof(tv)); 168 } else 169 TIMESPEC_TO_TIMEVAL(&tv, &diff); 170 tvp = &tv; 171 } 172 } 173 174 swait = NULL; 175 n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait); 176 177 if (n == 0 || call_timer_dispatch) { 178 /* 179 * We call isc_timermgr_dispatch() only when 180 * necessary, in order to reduce overhead. If the 181 * select() call indicates a timeout, we need the 182 * dispatch. Even if not, if we set the 0-timeout 183 * for the select() call, we need to check the timer 184 * events. In the 'readytasks' case, there may be no 185 * timeout event actually, but there is no other way 186 * to reduce the overhead. 187 * Note that we do not have to worry about the case 188 * where a new timer is inserted during the select() 189 * call, since this loop only runs in the non-thread 190 * mode. 191 */ 192 isc_timermgr_dispatch(ctx->timermgr); 193 } 194 if (n > 0) 195 (void)isc_socketmgr_dispatch(ctx->socketmgr, swait); 196 (void)isc_taskmgr_dispatch(ctx->taskmgr); 197 } 198 return (ISC_R_SUCCESS); 199 } 200 201 static isc_result_t 202 isc_app_ctxrun(isc_appctx_t *ctx) { 203 int result; 204 isc_event_t *event, *next_event; 205 isc_task_t *task; 206 207 if (!ctx->running) { 208 ctx->running = 1; 209 210 /* 211 * Post any on-run events (in FIFO order). 212 */ 213 for (event = ISC_LIST_HEAD(ctx->on_run); 214 event != NULL; 215 event = next_event) { 216 next_event = ISC_LIST_NEXT(event, ev_link); 217 ISC_LIST_UNLINK(ctx->on_run, event, ev_link); 218 task = event->ev_sender; 219 event->ev_sender = NULL; 220 isc_task_sendanddetach(&task, &event); 221 } 222 223 } 224 225 (void) isc_taskmgr_dispatch(ctx->taskmgr); 226 result = evloop(ctx); 227 return (result); 228 } 229 230 isc_result_t 231 isc_app_run(void) { 232 return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx)); 233 } 234 235 static isc_result_t 236 isc_app_ctxshutdown(isc_appctx_t *ctx) { 237 int want_kill = 1; 238 239 REQUIRE(ctx->running); 240 241 if (ctx->shutdown_requested) 242 want_kill = 0; 243 else 244 ctx->shutdown_requested = 1; 245 246 if (want_kill) { 247 if (ctx != &isc_g_appctx) 248 /* BIND9 internal, but using multiple contexts */ 249 ctx->want_shutdown = 1; 250 else { 251 ctx->want_shutdown = 1; 252 } 253 } 254 255 return (ISC_R_SUCCESS); 256 } 257 258 isc_result_t 259 isc_app_shutdown(void) { 260 return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx)); 261 } 262