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/boolean.h> 27 #include <isc/event.h> 28 29 #include <string.h> 30 #include <isc/task.h> 31 #include <isc/time.h> 32 #include <isc/util.h> 33 34 /*% 35 * For BIND9 internal applications built with threads, we use a single app 36 * context and let multiple worker, I/O, timer threads do actual jobs. 37 * For other cases (including BIND9 built without threads) an app context acts 38 * as an event loop dispatching various events. 39 */ 40 #include "../timer_p.h" 41 #include "../task_p.h" 42 #include "socket_p.h" 43 44 /* 45 * The application context of this module. This implementation actually 46 * doesn't use it. (This may change in the future). 47 */ 48 49 typedef struct isc_appctx { 50 isc_eventlist_t on_run; 51 isc_boolean_t shutdown_requested; 52 isc_boolean_t running; 53 54 /*! 55 * We assume that 'want_shutdown' can be read and written atomically. 56 */ 57 isc_boolean_t want_shutdown; 58 59 isc_taskmgr_t *taskmgr; 60 isc_socketmgr_t *socketmgr; 61 isc_timermgr_t *timermgr; 62 } isc_appctx_t; 63 64 static isc_appctx_t isc_g_appctx; 65 66 static isc_result_t isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task, 67 isc_taskaction_t action, void *arg); 68 69 static isc_result_t 70 isc_app_ctxstart(isc_appctx_t *ctx) { 71 /* 72 * Start an ISC library application. 73 */ 74 75 ISC_LIST_INIT(ctx->on_run); 76 77 ctx->shutdown_requested = ISC_FALSE; 78 ctx->running = ISC_FALSE; 79 ctx->want_shutdown = ISC_FALSE; 80 81 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { 82 UNEXPECTED_ERROR(__FILE__, __LINE__, 83 "isc_app_ctxstart() signal: %s", 84 strerror(errno)); 85 return ISC_R_UNEXPECTED; 86 } 87 return ISC_R_SUCCESS; 88 } 89 90 isc_result_t 91 isc_app_start(void) { 92 /* The remaining members will be initialized in ctxstart() */ 93 94 return (isc_app_ctxstart((isc_appctx_t *)&isc_g_appctx)); 95 } 96 97 isc_result_t 98 isc_app_onrun(isc_task_t *task, isc_taskaction_t action, 99 void *arg) 100 { 101 return (isc_app_ctxonrun((isc_appctx_t *)&isc_g_appctx, 102 task, action, arg)); 103 } 104 105 isc_result_t 106 isc_app_ctxonrun(isc_appctx_t *ctx, isc_task_t *task, 107 isc_taskaction_t action, void *arg) 108 { 109 isc_event_t *event; 110 isc_task_t *cloned_task = NULL; 111 isc_result_t result; 112 113 if (ctx->running) { 114 result = ISC_R_ALREADYRUNNING; 115 goto unlock; 116 } 117 118 /* 119 * Note that we store the task to which we're going to send the event 120 * in the event's "sender" field. 121 */ 122 isc_task_attach(task, &cloned_task); 123 event = isc_event_allocate(cloned_task, ISC_APPEVENT_SHUTDOWN, 124 action, arg, sizeof(*event)); 125 if (event == NULL) { 126 isc_task_detach(&cloned_task); 127 result = ISC_R_NOMEMORY; 128 goto unlock; 129 } 130 131 ISC_LIST_APPEND(ctx->on_run, event, ev_link); 132 133 result = ISC_R_SUCCESS; 134 135 unlock: 136 return (result); 137 } 138 139 /*! 140 * Event loop for nonthreaded programs. 141 */ 142 static isc_result_t 143 evloop(isc_appctx_t *ctx) { 144 isc_result_t result; 145 146 while (!ctx->want_shutdown) { 147 int n; 148 struct timespec when, now; 149 struct timeval tv, *tvp; 150 isc_socketwait_t *swait; 151 isc_boolean_t readytasks; 152 isc_boolean_t call_timer_dispatch = ISC_FALSE; 153 154 readytasks = isc_taskmgr_ready(ctx->taskmgr); 155 if (readytasks) { 156 tv.tv_sec = 0; 157 tv.tv_usec = 0; 158 tvp = &tv; 159 call_timer_dispatch = ISC_TRUE; 160 } else { 161 result = isc_timermgr_nextevent(ctx->timermgr, &when); 162 if (result != ISC_R_SUCCESS) 163 tvp = NULL; 164 else { 165 uint64_t us; 166 167 clock_gettime(CLOCK_MONOTONIC, &now); 168 us = isc_time_microdiff(&when, &now); 169 if (us == 0) 170 call_timer_dispatch = ISC_TRUE; 171 tv.tv_sec = us / 1000000; 172 tv.tv_usec = us % 1000000; 173 tvp = &tv; 174 } 175 } 176 177 swait = NULL; 178 n = isc_socketmgr_waitevents(ctx->socketmgr, tvp, &swait); 179 180 if (n == 0 || call_timer_dispatch) { 181 /* 182 * We call isc_timermgr_dispatch() only when 183 * necessary, in order to reduce overhead. If the 184 * select() call indicates a timeout, we need the 185 * dispatch. Even if not, if we set the 0-timeout 186 * for the select() call, we need to check the timer 187 * events. In the 'readytasks' case, there may be no 188 * timeout event actually, but there is no other way 189 * to reduce the overhead. 190 * Note that we do not have to worry about the case 191 * where a new timer is inserted during the select() 192 * call, since this loop only runs in the non-thread 193 * mode. 194 */ 195 isc_timermgr_dispatch(ctx->timermgr); 196 } 197 if (n > 0) 198 (void)isc_socketmgr_dispatch(ctx->socketmgr, swait); 199 (void)isc_taskmgr_dispatch(ctx->taskmgr); 200 } 201 return (ISC_R_SUCCESS); 202 } 203 204 static isc_result_t 205 isc_app_ctxrun(isc_appctx_t *ctx) { 206 int result; 207 isc_event_t *event, *next_event; 208 isc_task_t *task; 209 210 if (!ctx->running) { 211 ctx->running = ISC_TRUE; 212 213 /* 214 * Post any on-run events (in FIFO order). 215 */ 216 for (event = ISC_LIST_HEAD(ctx->on_run); 217 event != NULL; 218 event = next_event) { 219 next_event = ISC_LIST_NEXT(event, ev_link); 220 ISC_LIST_UNLINK(ctx->on_run, event, ev_link); 221 task = event->ev_sender; 222 event->ev_sender = NULL; 223 isc_task_sendanddetach(&task, &event); 224 } 225 226 } 227 228 (void) isc_taskmgr_dispatch(ctx->taskmgr); 229 result = evloop(ctx); 230 return (result); 231 } 232 233 isc_result_t 234 isc_app_run(void) { 235 return (isc_app_ctxrun((isc_appctx_t *)&isc_g_appctx)); 236 } 237 238 static isc_result_t 239 isc_app_ctxshutdown(isc_appctx_t *ctx) { 240 isc_boolean_t want_kill = ISC_TRUE; 241 242 REQUIRE(ctx->running); 243 244 if (ctx->shutdown_requested) 245 want_kill = ISC_FALSE; 246 else 247 ctx->shutdown_requested = ISC_TRUE; 248 249 if (want_kill) { 250 if (ctx != &isc_g_appctx) 251 /* BIND9 internal, but using multiple contexts */ 252 ctx->want_shutdown = ISC_TRUE; 253 else { 254 ctx->want_shutdown = ISC_TRUE; 255 } 256 } 257 258 return (ISC_R_SUCCESS); 259 } 260 261 isc_result_t 262 isc_app_shutdown(void) { 263 return (isc_app_ctxshutdown((isc_appctx_t *)&isc_g_appctx)); 264 } 265