1 1.1 christos /* $NetBSD: task.c,v 1.2 2024/08/18 20:47:14 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * Copyright (C) 1998-2003 Internet Software Consortium. 6 1.1 christos * 7 1.1 christos * Permission to use, copy, modify, and/or distribute this software for any 8 1.1 christos * purpose with or without fee is hereby granted, provided that the above 9 1.1 christos * copyright notice and this permission notice appear in all copies. 10 1.1 christos * 11 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 1.1 christos * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 1.1 christos * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 1.1 christos * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 1.1 christos * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 1.1 christos * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 1.1 christos * PERFORMANCE OF THIS SOFTWARE. 18 1.1 christos */ 19 1.1 christos 20 1.1 christos /* Id */ 21 1.1 christos 22 1.1 christos /*! \file 23 1.1 christos * \author Principal Author: Bob Halley 24 1.1 christos */ 25 1.1 christos 26 1.1 christos /* 27 1.1 christos * XXXRTH Need to document the states a task can be in, and the rules 28 1.1 christos * for changing states. 29 1.1 christos */ 30 1.1 christos 31 1.1 christos #include <config.h> 32 1.1 christos 33 1.1 christos #include <isc/condition.h> 34 1.1 christos #include <isc/event.h> 35 1.1 christos #include <isc/magic.h> 36 1.1 christos #include <isc/mem.h> 37 1.1 christos #include <isc/msgs.h> 38 1.1 christos #include <isc/platform.h> 39 1.1 christos #include <isc/string.h> 40 1.1 christos #include <isc/task.h> 41 1.1 christos #include <isc/thread.h> 42 1.1 christos #include <isc/util.h> 43 1.1 christos #include <isc/xml.h> 44 1.1 christos 45 1.1 christos #ifdef OPENSSL_LEAKS 46 1.1 christos #include <openssl/err.h> 47 1.1 christos #endif 48 1.1 christos 49 1.1 christos /*% 50 1.1 christos * For BIND9 internal applications: 51 1.1 christos * when built with threads we use multiple worker threads shared by the whole 52 1.1 christos * application. 53 1.1 christos * when built without threads we share a single global task manager and use 54 1.1 christos * an integrated event loop for socket, timer, and other generic task events. 55 1.1 christos * For generic library: 56 1.1 christos * we don't use either of them: an application can have multiple task managers 57 1.1 christos * whether or not it's threaded, and if the application is threaded each thread 58 1.1 christos * is expected to have a separate manager; no "worker threads" are shared by 59 1.1 christos * the application threads. 60 1.1 christos */ 61 1.1 christos #ifdef BIND9 62 1.1 christos #ifdef ISC_PLATFORM_USETHREADS 63 1.1 christos #define USE_WORKER_THREADS 64 1.1 christos #else 65 1.1 christos #define USE_SHARED_MANAGER 66 1.1 christos #endif /* ISC_PLATFORM_USETHREADS */ 67 1.1 christos #endif /* BIND9 */ 68 1.1 christos 69 1.1 christos #include "task_p.h" 70 1.1 christos 71 1.1 christos #ifdef ISC_TASK_TRACE 72 1.1 christos #define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ 73 1.1 christos task, isc_thread_self(), (m)) 74 1.1 christos #define XTTRACE(t, m) fprintf(stderr, "task %p thread %lu: %s\n", \ 75 1.1 christos (t), isc_thread_self(), (m)) 76 1.1 christos #define XTHREADTRACE(m) fprintf(stderr, "thread %lu: %s\n", \ 77 1.1 christos isc_thread_self(), (m)) 78 1.1 christos #else 79 1.1 christos #define XTRACE(m) 80 1.1 christos #define XTTRACE(t, m) 81 1.1 christos #define XTHREADTRACE(m) 82 1.1 christos #endif 83 1.1 christos 84 1.1 christos /*** 85 1.1 christos *** Types. 86 1.1 christos ***/ 87 1.1 christos 88 1.1 christos typedef enum { 89 1.1 christos task_state_idle, task_state_ready, task_state_running, 90 1.1 christos task_state_done 91 1.1 christos } task_state_t; 92 1.1 christos 93 1.1 christos #if defined(HAVE_LIBXML2) && defined(BIND9) 94 1.1 christos static const char *statenames[] = { 95 1.1 christos "idle", "ready", "running", "done", 96 1.1 christos }; 97 1.1 christos #endif 98 1.1 christos 99 1.1 christos #define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K') 100 1.1 christos #define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC) 101 1.1 christos 102 1.1 christos typedef struct isc__task isc__task_t; 103 1.1 christos typedef struct isc__taskmgr isc__taskmgr_t; 104 1.1 christos 105 1.1 christos struct isc__task { 106 1.1 christos /* Not locked. */ 107 1.1 christos isc_task_t common; 108 1.1 christos isc__taskmgr_t * manager; 109 1.1 christos isc_mutex_t lock; 110 1.1 christos /* Locked by task lock. */ 111 1.1 christos task_state_t state; 112 1.1 christos unsigned int references; 113 1.1 christos isc_eventlist_t events; 114 1.1 christos isc_eventlist_t on_shutdown; 115 1.1 christos unsigned int quantum; 116 1.1 christos unsigned int flags; 117 1.1 christos isc_stdtime_t now; 118 1.1 christos char name[16]; 119 1.1 christos void * tag; 120 1.1 christos /* Locked by task manager lock. */ 121 1.1 christos LINK(isc__task_t) link; 122 1.1 christos LINK(isc__task_t) ready_link; 123 1.1 christos LINK(isc__task_t) ready_priority_link; 124 1.1 christos }; 125 1.1 christos 126 1.1 christos #define TASK_F_SHUTTINGDOWN 0x01 127 1.1 christos #define TASK_F_PRIVILEGED 0x02 128 1.1 christos 129 1.1 christos #define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ 130 1.1 christos != 0) 131 1.1 christos 132 1.1 christos #define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M') 133 1.1 christos #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC) 134 1.1 christos 135 1.1 christos typedef ISC_LIST(isc__task_t) isc__tasklist_t; 136 1.1 christos 137 1.1 christos struct isc__taskmgr { 138 1.1 christos /* Not locked. */ 139 1.1 christos isc_taskmgr_t common; 140 1.1 christos isc_mem_t * mctx; 141 1.1 christos isc_mutex_t lock; 142 1.1 christos #ifdef ISC_PLATFORM_USETHREADS 143 1.1 christos unsigned int workers; 144 1.1 christos isc_thread_t * threads; 145 1.1 christos #endif /* ISC_PLATFORM_USETHREADS */ 146 1.1 christos /* Locked by task manager lock. */ 147 1.1 christos unsigned int default_quantum; 148 1.1 christos LIST(isc__task_t) tasks; 149 1.1 christos isc__tasklist_t ready_tasks; 150 1.1 christos isc__tasklist_t ready_priority_tasks; 151 1.1 christos isc_taskmgrmode_t mode; 152 1.1 christos #ifdef ISC_PLATFORM_USETHREADS 153 1.1 christos isc_condition_t work_available; 154 1.1 christos isc_condition_t exclusive_granted; 155 1.1 christos isc_condition_t paused; 156 1.1 christos #endif /* ISC_PLATFORM_USETHREADS */ 157 1.1 christos unsigned int tasks_running; 158 1.1 christos isc_boolean_t pause_requested; 159 1.1 christos isc_boolean_t exclusive_requested; 160 1.1 christos isc_boolean_t exiting; 161 1.1 christos #ifdef USE_SHARED_MANAGER 162 1.1 christos unsigned int refs; 163 1.1 christos #endif /* ISC_PLATFORM_USETHREADS */ 164 1.1 christos }; 165 1.1 christos 166 1.1 christos #define DEFAULT_TASKMGR_QUANTUM 10 167 1.1 christos #define DEFAULT_DEFAULT_QUANTUM 5 168 1.1 christos #define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) 169 1.1 christos 170 1.1 christos #ifdef USE_SHARED_MANAGER 171 1.1 christos static isc__taskmgr_t *taskmgr = NULL; 172 1.1 christos #endif /* USE_SHARED_MANAGER */ 173 1.1 christos 174 1.1 christos /*% 175 1.1 christos * The following can be either static or public, depending on build environment. 176 1.1 christos */ 177 1.1 christos 178 1.1 christos #ifdef BIND9 179 1.1 christos #define ISC_TASKFUNC_SCOPE 180 1.1 christos #else 181 1.1 christos #define ISC_TASKFUNC_SCOPE static 182 1.1 christos #endif 183 1.1 christos 184 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 185 1.1 christos isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, 186 1.1 christos isc_task_t **taskp); 187 1.1 christos ISC_TASKFUNC_SCOPE void 188 1.1 christos isc__task_attach(isc_task_t *source0, isc_task_t **targetp); 189 1.1 christos ISC_TASKFUNC_SCOPE void 190 1.1 christos isc__task_detach(isc_task_t **taskp); 191 1.1 christos ISC_TASKFUNC_SCOPE void 192 1.1 christos isc__task_send(isc_task_t *task0, isc_event_t **eventp); 193 1.1 christos ISC_TASKFUNC_SCOPE void 194 1.1 christos isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp); 195 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 196 1.1 christos isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, 197 1.1 christos isc_eventtype_t last, void *tag); 198 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 199 1.1 christos isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, 200 1.1 christos void *tag); 201 1.1 christos ISC_TASKFUNC_SCOPE isc_boolean_t 202 1.1 christos isc__task_purgeevent(isc_task_t *task0, isc_event_t *event); 203 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 204 1.1 christos isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, 205 1.1 christos isc_eventtype_t last, void *tag, 206 1.1 christos isc_eventlist_t *events); 207 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 208 1.1 christos isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, 209 1.1 christos void *tag, isc_eventlist_t *events); 210 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 211 1.1 christos isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, 212 1.1 christos const void *arg); 213 1.1 christos ISC_TASKFUNC_SCOPE void 214 1.1 christos isc__task_shutdown(isc_task_t *task0); 215 1.1 christos ISC_TASKFUNC_SCOPE void 216 1.1 christos isc__task_destroy(isc_task_t **taskp); 217 1.1 christos ISC_TASKFUNC_SCOPE void 218 1.1 christos isc__task_setname(isc_task_t *task0, const char *name, void *tag); 219 1.1 christos ISC_TASKFUNC_SCOPE const char * 220 1.1 christos isc__task_getname(isc_task_t *task0); 221 1.1 christos ISC_TASKFUNC_SCOPE void * 222 1.1 christos isc__task_gettag(isc_task_t *task0); 223 1.1 christos ISC_TASKFUNC_SCOPE void 224 1.1 christos isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t); 225 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 226 1.1 christos isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, 227 1.1 christos unsigned int default_quantum, isc_taskmgr_t **managerp); 228 1.1 christos ISC_TASKFUNC_SCOPE void 229 1.1 christos isc__taskmgr_destroy(isc_taskmgr_t **managerp); 230 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 231 1.1 christos isc__task_beginexclusive(isc_task_t *task); 232 1.1 christos ISC_TASKFUNC_SCOPE void 233 1.1 christos isc__task_endexclusive(isc_task_t *task0); 234 1.1 christos ISC_TASKFUNC_SCOPE void 235 1.1 christos isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv); 236 1.1 christos ISC_TASKFUNC_SCOPE isc_boolean_t 237 1.1 christos isc__task_privilege(isc_task_t *task0); 238 1.1 christos ISC_TASKFUNC_SCOPE void 239 1.1 christos isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode); 240 1.1 christos ISC_TASKFUNC_SCOPE isc_taskmgrmode_t 241 1.1 christos isc__taskmgr_mode(isc_taskmgr_t *manager0); 242 1.1 christos 243 1.1 christos static inline isc_boolean_t 244 1.1 christos empty_readyq(isc__taskmgr_t *manager); 245 1.1 christos 246 1.1 christos static inline isc__task_t * 247 1.1 christos pop_readyq(isc__taskmgr_t *manager); 248 1.1 christos 249 1.1 christos static inline void 250 1.1 christos push_readyq(isc__taskmgr_t *manager, isc__task_t *task); 251 1.1 christos 252 1.1 christos static struct isc__taskmethods { 253 1.1 christos isc_taskmethods_t methods; 254 1.1 christos 255 1.1 christos /*% 256 1.1 christos * The following are defined just for avoiding unused static functions. 257 1.1 christos */ 258 1.1 christos #ifndef BIND9 259 1.1 christos void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime; 260 1.1 christos #endif 261 1.1 christos } taskmethods = { 262 1.1 christos { 263 1.1 christos isc__task_attach, 264 1.1 christos isc__task_detach, 265 1.1 christos isc__task_destroy, 266 1.1 christos isc__task_send, 267 1.1 christos isc__task_sendanddetach, 268 1.1 christos isc__task_unsend, 269 1.1 christos isc__task_onshutdown, 270 1.1 christos isc__task_shutdown, 271 1.1 christos isc__task_setname, 272 1.1 christos isc__task_purge, 273 1.1 christos isc__task_purgerange, 274 1.1 christos isc__task_beginexclusive, 275 1.1 christos isc__task_endexclusive, 276 1.1 christos isc__task_setprivilege, 277 1.1 christos isc__task_privilege 278 1.1 christos } 279 1.1 christos #ifndef BIND9 280 1.1 christos , 281 1.1 christos (void *)isc__task_purgeevent, (void *)isc__task_unsendrange, 282 1.1 christos (void *)isc__task_getname, (void *)isc__task_gettag, 283 1.1 christos (void *)isc__task_getcurrenttime 284 1.1 christos #endif 285 1.1 christos }; 286 1.1 christos 287 1.1 christos static isc_taskmgrmethods_t taskmgrmethods = { 288 1.1 christos isc__taskmgr_destroy, 289 1.1 christos isc__taskmgr_setmode, 290 1.1 christos isc__taskmgr_mode, 291 1.1 christos isc__task_create 292 1.1 christos }; 293 1.1 christos 294 1.1 christos /*** 295 1.1 christos *** Tasks. 296 1.1 christos ***/ 297 1.1 christos 298 1.1 christos static void 299 1.1 christos task_finished(isc__task_t *task) { 300 1.1 christos isc__taskmgr_t *manager = task->manager; 301 1.1 christos 302 1.1 christos REQUIRE(EMPTY(task->events)); 303 1.1 christos REQUIRE(EMPTY(task->on_shutdown)); 304 1.1 christos REQUIRE(task->references == 0); 305 1.1 christos REQUIRE(task->state == task_state_done); 306 1.1 christos 307 1.1 christos XTRACE("task_finished"); 308 1.1 christos 309 1.1 christos LOCK(&manager->lock); 310 1.1 christos UNLINK(manager->tasks, task, link); 311 1.1 christos #ifdef USE_WORKER_THREADS 312 1.1 christos if (FINISHED(manager)) { 313 1.1 christos /* 314 1.1 christos * All tasks have completed and the 315 1.1 christos * task manager is exiting. Wake up 316 1.1 christos * any idle worker threads so they 317 1.1 christos * can exit. 318 1.1 christos */ 319 1.1 christos BROADCAST(&manager->work_available); 320 1.1 christos } 321 1.1 christos #endif /* USE_WORKER_THREADS */ 322 1.1 christos UNLOCK(&manager->lock); 323 1.1 christos 324 1.1 christos DESTROYLOCK(&task->lock); 325 1.1 christos task->common.impmagic = 0; 326 1.1 christos task->common.magic = 0; 327 1.1 christos isc_mem_put(manager->mctx, task, sizeof(*task)); 328 1.1 christos } 329 1.1 christos 330 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 331 1.1 christos isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, 332 1.1 christos isc_task_t **taskp) 333 1.1 christos { 334 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 335 1.1 christos isc__task_t *task; 336 1.1 christos isc_boolean_t exiting; 337 1.1 christos isc_result_t result; 338 1.1 christos 339 1.1 christos REQUIRE(VALID_MANAGER(manager)); 340 1.1 christos REQUIRE(taskp != NULL && *taskp == NULL); 341 1.1 christos 342 1.1 christos task = isc_mem_get(manager->mctx, sizeof(*task)); 343 1.1 christos if (task == NULL) 344 1.1 christos return (ISC_R_NOMEMORY); 345 1.1 christos XTRACE("isc_task_create"); 346 1.1 christos result = isc_mutex_init(&task->lock); 347 1.1 christos if (result != ISC_R_SUCCESS) { 348 1.1 christos isc_mem_put(manager->mctx, task, sizeof(*task)); 349 1.1 christos return (result); 350 1.1 christos } 351 1.1 christos LOCK(&manager->lock); 352 1.1 christos LOCK(&task->lock); /* helps coverity analysis noise ratio */ 353 1.1 christos task->manager = manager; 354 1.1 christos task->state = task_state_idle; 355 1.1 christos task->references = 1; 356 1.1 christos INIT_LIST(task->events); 357 1.1 christos INIT_LIST(task->on_shutdown); 358 1.1 christos task->quantum = quantum; 359 1.1 christos task->flags = 0; 360 1.1 christos task->now = 0; 361 1.1 christos memset(task->name, 0, sizeof(task->name)); 362 1.1 christos task->tag = NULL; 363 1.1 christos INIT_LINK(task, link); 364 1.1 christos INIT_LINK(task, ready_link); 365 1.1 christos INIT_LINK(task, ready_priority_link); 366 1.1 christos UNLOCK(&task->lock); 367 1.1 christos UNLOCK(&manager->lock); 368 1.1 christos 369 1.1 christos exiting = ISC_FALSE; 370 1.1 christos LOCK(&manager->lock); 371 1.1 christos if (!manager->exiting) { 372 1.1 christos if (task->quantum == 0) 373 1.1 christos task->quantum = manager->default_quantum; 374 1.1 christos APPEND(manager->tasks, task, link); 375 1.1 christos } else 376 1.1 christos exiting = ISC_TRUE; 377 1.1 christos UNLOCK(&manager->lock); 378 1.1 christos 379 1.1 christos if (exiting) { 380 1.1 christos DESTROYLOCK(&task->lock); 381 1.1 christos isc_mem_put(manager->mctx, task, sizeof(*task)); 382 1.1 christos return (ISC_R_SHUTTINGDOWN); 383 1.1 christos } 384 1.1 christos 385 1.1 christos task->common.methods = (isc_taskmethods_t *)&taskmethods; 386 1.1 christos task->common.magic = ISCAPI_TASK_MAGIC; 387 1.1 christos task->common.impmagic = TASK_MAGIC; 388 1.1 christos *taskp = (isc_task_t *)task; 389 1.1 christos 390 1.1 christos return (ISC_R_SUCCESS); 391 1.1 christos } 392 1.1 christos 393 1.1 christos ISC_TASKFUNC_SCOPE void 394 1.1 christos isc__task_attach(isc_task_t *source0, isc_task_t **targetp) { 395 1.1 christos isc__task_t *source = (isc__task_t *)source0; 396 1.1 christos 397 1.1 christos /* 398 1.1 christos * Attach *targetp to source. 399 1.1 christos */ 400 1.1 christos 401 1.1 christos REQUIRE(VALID_TASK(source)); 402 1.1 christos REQUIRE(targetp != NULL && *targetp == NULL); 403 1.1 christos 404 1.1 christos XTTRACE(source, "isc_task_attach"); 405 1.1 christos 406 1.1 christos LOCK(&source->lock); 407 1.1 christos source->references++; 408 1.1 christos UNLOCK(&source->lock); 409 1.1 christos 410 1.1 christos *targetp = (isc_task_t *)source; 411 1.1 christos } 412 1.1 christos 413 1.1 christos static inline isc_boolean_t 414 1.1 christos task_shutdown(isc__task_t *task) { 415 1.1 christos isc_boolean_t was_idle = ISC_FALSE; 416 1.1 christos isc_event_t *event, *prev; 417 1.1 christos 418 1.1 christos /* 419 1.1 christos * Caller must be holding the task's lock. 420 1.1 christos */ 421 1.1 christos 422 1.1 christos XTRACE("task_shutdown"); 423 1.1 christos 424 1.1 christos if (! TASK_SHUTTINGDOWN(task)) { 425 1.1 christos XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 426 1.1 christos ISC_MSG_SHUTTINGDOWN, "shutting down")); 427 1.1 christos task->flags |= TASK_F_SHUTTINGDOWN; 428 1.1 christos if (task->state == task_state_idle) { 429 1.1 christos INSIST(EMPTY(task->events)); 430 1.1 christos task->state = task_state_ready; 431 1.1 christos was_idle = ISC_TRUE; 432 1.1 christos } 433 1.1 christos INSIST(task->state == task_state_ready || 434 1.1 christos task->state == task_state_running); 435 1.1 christos 436 1.1 christos /* 437 1.1 christos * Note that we post shutdown events LIFO. 438 1.1 christos */ 439 1.1 christos for (event = TAIL(task->on_shutdown); 440 1.1 christos event != NULL; 441 1.1 christos event = prev) { 442 1.1 christos prev = PREV(event, ev_link); 443 1.1 christos DEQUEUE(task->on_shutdown, event, ev_link); 444 1.1 christos ENQUEUE(task->events, event, ev_link); 445 1.1 christos } 446 1.1 christos } 447 1.1 christos 448 1.1 christos return (was_idle); 449 1.1 christos } 450 1.1 christos 451 1.1 christos /* 452 1.1 christos * Moves a task onto the appropriate run queue. 453 1.1 christos * 454 1.1 christos * Caller must NOT hold manager lock. 455 1.1 christos */ 456 1.1 christos static inline void 457 1.1 christos task_ready(isc__task_t *task) { 458 1.1 christos isc__taskmgr_t *manager = task->manager; 459 1.1 christos #ifdef USE_WORKER_THREADS 460 1.1 christos isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task); 461 1.1 christos #endif /* USE_WORKER_THREADS */ 462 1.1 christos 463 1.1 christos REQUIRE(VALID_MANAGER(manager)); 464 1.1 christos REQUIRE(task->state == task_state_ready); 465 1.1 christos 466 1.1 christos XTRACE("task_ready"); 467 1.1 christos 468 1.1 christos LOCK(&manager->lock); 469 1.1 christos push_readyq(manager, task); 470 1.1 christos #ifdef USE_WORKER_THREADS 471 1.1 christos if (manager->mode == isc_taskmgrmode_normal || has_privilege) 472 1.1 christos SIGNAL(&manager->work_available); 473 1.1 christos #endif /* USE_WORKER_THREADS */ 474 1.1 christos UNLOCK(&manager->lock); 475 1.1 christos } 476 1.1 christos 477 1.1 christos static inline isc_boolean_t 478 1.1 christos task_detach(isc__task_t *task) { 479 1.1 christos 480 1.1 christos /* 481 1.1 christos * Caller must be holding the task lock. 482 1.1 christos */ 483 1.1 christos 484 1.1 christos REQUIRE(task->references > 0); 485 1.1 christos 486 1.1 christos XTRACE("detach"); 487 1.1 christos 488 1.1 christos task->references--; 489 1.1 christos if (task->references == 0 && task->state == task_state_idle) { 490 1.1 christos INSIST(EMPTY(task->events)); 491 1.1 christos /* 492 1.1 christos * There are no references to this task, and no 493 1.1 christos * pending events. We could try to optimize and 494 1.1 christos * either initiate shutdown or clean up the task, 495 1.1 christos * depending on its state, but it's easier to just 496 1.1 christos * make the task ready and allow run() or the event 497 1.1 christos * loop to deal with shutting down and termination. 498 1.1 christos */ 499 1.1 christos task->state = task_state_ready; 500 1.1 christos return (ISC_TRUE); 501 1.1 christos } 502 1.1 christos 503 1.1 christos return (ISC_FALSE); 504 1.1 christos } 505 1.1 christos 506 1.1 christos ISC_TASKFUNC_SCOPE void 507 1.1 christos isc__task_detach(isc_task_t **taskp) { 508 1.1 christos isc__task_t *task; 509 1.1 christos isc_boolean_t was_idle; 510 1.1 christos 511 1.1 christos /* 512 1.1 christos * Detach *taskp from its task. 513 1.1 christos */ 514 1.1 christos 515 1.1 christos REQUIRE(taskp != NULL); 516 1.1 christos task = (isc__task_t *)*taskp; 517 1.1 christos REQUIRE(VALID_TASK(task)); 518 1.1 christos 519 1.1 christos XTRACE("isc_task_detach"); 520 1.1 christos 521 1.1 christos LOCK(&task->lock); 522 1.1 christos was_idle = task_detach(task); 523 1.1 christos UNLOCK(&task->lock); 524 1.1 christos 525 1.1 christos if (was_idle) 526 1.1 christos task_ready(task); 527 1.1 christos 528 1.1 christos *taskp = NULL; 529 1.1 christos } 530 1.1 christos 531 1.1 christos static inline isc_boolean_t 532 1.1 christos task_send(isc__task_t *task, isc_event_t **eventp) { 533 1.1 christos isc_boolean_t was_idle = ISC_FALSE; 534 1.1 christos isc_event_t *event; 535 1.1 christos 536 1.1 christos /* 537 1.1 christos * Caller must be holding the task lock. 538 1.1 christos */ 539 1.1 christos 540 1.1 christos REQUIRE(eventp != NULL); 541 1.1 christos event = *eventp; 542 1.1 christos REQUIRE(event != NULL); 543 1.1 christos REQUIRE(event->ev_type > 0); 544 1.1 christos REQUIRE(task->state != task_state_done); 545 1.1 christos 546 1.1 christos XTRACE("task_send"); 547 1.1 christos 548 1.1 christos if (task->state == task_state_idle) { 549 1.1 christos was_idle = ISC_TRUE; 550 1.1 christos INSIST(EMPTY(task->events)); 551 1.1 christos task->state = task_state_ready; 552 1.1 christos } 553 1.1 christos INSIST(task->state == task_state_ready || 554 1.1 christos task->state == task_state_running); 555 1.1 christos ENQUEUE(task->events, event, ev_link); 556 1.1 christos *eventp = NULL; 557 1.1 christos 558 1.1 christos return (was_idle); 559 1.1 christos } 560 1.1 christos 561 1.1 christos ISC_TASKFUNC_SCOPE void 562 1.1 christos isc__task_send(isc_task_t *task0, isc_event_t **eventp) { 563 1.1 christos isc__task_t *task = (isc__task_t *)task0; 564 1.1 christos isc_boolean_t was_idle; 565 1.1 christos 566 1.1 christos /* 567 1.1 christos * Send '*event' to 'task'. 568 1.1 christos */ 569 1.1 christos 570 1.1 christos REQUIRE(VALID_TASK(task)); 571 1.1 christos 572 1.1 christos XTRACE("isc_task_send"); 573 1.1 christos 574 1.1 christos /* 575 1.1 christos * We're trying hard to hold locks for as short a time as possible. 576 1.1 christos * We're also trying to hold as few locks as possible. This is why 577 1.1 christos * some processing is deferred until after the lock is released. 578 1.1 christos */ 579 1.1 christos LOCK(&task->lock); 580 1.1 christos was_idle = task_send(task, eventp); 581 1.1 christos UNLOCK(&task->lock); 582 1.1 christos 583 1.1 christos if (was_idle) { 584 1.1 christos /* 585 1.1 christos * We need to add this task to the ready queue. 586 1.1 christos * 587 1.1 christos * We've waited until now to do it because making a task 588 1.1 christos * ready requires locking the manager. If we tried to do 589 1.1 christos * this while holding the task lock, we could deadlock. 590 1.1 christos * 591 1.1 christos * We've changed the state to ready, so no one else will 592 1.1 christos * be trying to add this task to the ready queue. The 593 1.1 christos * only way to leave the ready state is by executing the 594 1.1 christos * task. It thus doesn't matter if events are added, 595 1.1 christos * removed, or a shutdown is started in the interval 596 1.1 christos * between the time we released the task lock, and the time 597 1.1 christos * we add the task to the ready queue. 598 1.1 christos */ 599 1.1 christos task_ready(task); 600 1.1 christos } 601 1.1 christos } 602 1.1 christos 603 1.1 christos ISC_TASKFUNC_SCOPE void 604 1.1 christos isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { 605 1.1 christos isc_boolean_t idle1, idle2; 606 1.1 christos isc__task_t *task; 607 1.1 christos 608 1.1 christos /* 609 1.1 christos * Send '*event' to '*taskp' and then detach '*taskp' from its 610 1.1 christos * task. 611 1.1 christos */ 612 1.1 christos 613 1.1 christos REQUIRE(taskp != NULL); 614 1.1 christos task = (isc__task_t *)*taskp; 615 1.1 christos REQUIRE(VALID_TASK(task)); 616 1.1 christos 617 1.1 christos XTRACE("isc_task_sendanddetach"); 618 1.1 christos 619 1.1 christos LOCK(&task->lock); 620 1.1 christos idle1 = task_send(task, eventp); 621 1.1 christos idle2 = task_detach(task); 622 1.1 christos UNLOCK(&task->lock); 623 1.1 christos 624 1.1 christos /* 625 1.1 christos * If idle1, then idle2 shouldn't be true as well since we're holding 626 1.1 christos * the task lock, and thus the task cannot switch from ready back to 627 1.1 christos * idle. 628 1.1 christos */ 629 1.1 christos INSIST(!(idle1 && idle2)); 630 1.1 christos 631 1.1 christos if (idle1 || idle2) 632 1.1 christos task_ready(task); 633 1.1 christos 634 1.1 christos *taskp = NULL; 635 1.1 christos } 636 1.1 christos 637 1.1 christos #define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0) 638 1.1 christos 639 1.1 christos static unsigned int 640 1.1 christos dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first, 641 1.1 christos isc_eventtype_t last, void *tag, 642 1.1 christos isc_eventlist_t *events, isc_boolean_t purging) 643 1.1 christos { 644 1.1 christos isc_event_t *event, *next_event; 645 1.1 christos unsigned int count = 0; 646 1.1 christos 647 1.1 christos REQUIRE(VALID_TASK(task)); 648 1.1 christos REQUIRE(last >= first); 649 1.1 christos 650 1.1 christos XTRACE("dequeue_events"); 651 1.1 christos 652 1.1 christos /* 653 1.1 christos * Events matching 'sender', whose type is >= first and <= last, and 654 1.1 christos * whose tag is 'tag' will be dequeued. If 'purging', matching events 655 1.1 christos * which are marked as unpurgable will not be dequeued. 656 1.1 christos * 657 1.1 christos * sender == NULL means "any sender", and tag == NULL means "any tag". 658 1.1 christos */ 659 1.1 christos 660 1.1 christos LOCK(&task->lock); 661 1.1 christos 662 1.1 christos for (event = HEAD(task->events); event != NULL; event = next_event) { 663 1.1 christos next_event = NEXT(event, ev_link); 664 1.1 christos if (event->ev_type >= first && event->ev_type <= last && 665 1.1 christos (sender == NULL || event->ev_sender == sender) && 666 1.1 christos (tag == NULL || event->ev_tag == tag) && 667 1.1 christos (!purging || PURGE_OK(event))) { 668 1.1 christos DEQUEUE(task->events, event, ev_link); 669 1.1 christos ENQUEUE(*events, event, ev_link); 670 1.1 christos count++; 671 1.1 christos } 672 1.1 christos } 673 1.1 christos 674 1.1 christos UNLOCK(&task->lock); 675 1.1 christos 676 1.1 christos return (count); 677 1.1 christos } 678 1.1 christos 679 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 680 1.1 christos isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, 681 1.1 christos isc_eventtype_t last, void *tag) 682 1.1 christos { 683 1.1 christos isc__task_t *task = (isc__task_t *)task0; 684 1.1 christos unsigned int count; 685 1.1 christos isc_eventlist_t events; 686 1.1 christos isc_event_t *event, *next_event; 687 1.1 christos 688 1.1 christos /* 689 1.1 christos * Purge events from a task's event queue. 690 1.1 christos */ 691 1.1 christos 692 1.1 christos XTRACE("isc_task_purgerange"); 693 1.1 christos 694 1.1 christos ISC_LIST_INIT(events); 695 1.1 christos 696 1.1 christos count = dequeue_events(task, sender, first, last, tag, &events, 697 1.1 christos ISC_TRUE); 698 1.1 christos 699 1.1 christos for (event = HEAD(events); event != NULL; event = next_event) { 700 1.1 christos next_event = NEXT(event, ev_link); 701 1.1 christos isc_event_free(&event); 702 1.1 christos } 703 1.1 christos 704 1.1 christos /* 705 1.1 christos * Note that purging never changes the state of the task. 706 1.1 christos */ 707 1.1 christos 708 1.1 christos return (count); 709 1.1 christos } 710 1.1 christos 711 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 712 1.1 christos isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, 713 1.1 christos void *tag) 714 1.1 christos { 715 1.1 christos /* 716 1.1 christos * Purge events from a task's event queue. 717 1.1 christos */ 718 1.1 christos 719 1.1 christos XTRACE("isc_task_purge"); 720 1.1 christos 721 1.1 christos return (isc__task_purgerange(task, sender, type, type, tag)); 722 1.1 christos } 723 1.1 christos 724 1.1 christos ISC_TASKFUNC_SCOPE isc_boolean_t 725 1.1 christos isc__task_purgeevent(isc_task_t *task0, isc_event_t *event) { 726 1.1 christos isc__task_t *task = (isc__task_t *)task0; 727 1.1 christos isc_event_t *curr_event, *next_event; 728 1.1 christos 729 1.1 christos /* 730 1.1 christos * Purge 'event' from a task's event queue. 731 1.1 christos * 732 1.1 christos * XXXRTH: WARNING: This method may be removed before beta. 733 1.1 christos */ 734 1.1 christos 735 1.1 christos REQUIRE(VALID_TASK(task)); 736 1.1 christos 737 1.1 christos /* 738 1.1 christos * If 'event' is on the task's event queue, it will be purged, 739 1.1 christos * unless it is marked as unpurgeable. 'event' does not have to be 740 1.1 christos * on the task's event queue; in fact, it can even be an invalid 741 1.1 christos * pointer. Purging only occurs if the event is actually on the task's 742 1.1 christos * event queue. 743 1.1 christos * 744 1.1 christos * Purging never changes the state of the task. 745 1.1 christos */ 746 1.1 christos 747 1.1 christos LOCK(&task->lock); 748 1.1 christos for (curr_event = HEAD(task->events); 749 1.1 christos curr_event != NULL; 750 1.1 christos curr_event = next_event) { 751 1.1 christos next_event = NEXT(curr_event, ev_link); 752 1.1 christos if (curr_event == event && PURGE_OK(event)) { 753 1.1 christos DEQUEUE(task->events, curr_event, ev_link); 754 1.1 christos break; 755 1.1 christos } 756 1.1 christos } 757 1.1 christos UNLOCK(&task->lock); 758 1.1 christos 759 1.1 christos if (curr_event == NULL) 760 1.1 christos return (ISC_FALSE); 761 1.1 christos 762 1.1 christos isc_event_free(&curr_event); 763 1.1 christos 764 1.1 christos return (ISC_TRUE); 765 1.1 christos } 766 1.1 christos 767 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 768 1.1 christos isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, 769 1.1 christos isc_eventtype_t last, void *tag, 770 1.1 christos isc_eventlist_t *events) 771 1.1 christos { 772 1.1 christos /* 773 1.1 christos * Remove events from a task's event queue. 774 1.1 christos */ 775 1.1 christos 776 1.1 christos XTRACE("isc_task_unsendrange"); 777 1.1 christos 778 1.1 christos return (dequeue_events((isc__task_t *)task, sender, first, 779 1.1 christos last, tag, events, ISC_FALSE)); 780 1.1 christos } 781 1.1 christos 782 1.1 christos ISC_TASKFUNC_SCOPE unsigned int 783 1.1 christos isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, 784 1.1 christos void *tag, isc_eventlist_t *events) 785 1.1 christos { 786 1.1 christos /* 787 1.1 christos * Remove events from a task's event queue. 788 1.1 christos */ 789 1.1 christos 790 1.1 christos XTRACE("isc_task_unsend"); 791 1.1 christos 792 1.1 christos return (dequeue_events((isc__task_t *)task, sender, type, 793 1.1 christos type, tag, events, ISC_FALSE)); 794 1.1 christos } 795 1.1 christos 796 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 797 1.1 christos isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, 798 1.1 christos const void *arg) 799 1.1 christos { 800 1.1 christos isc__task_t *task = (isc__task_t *)task0; 801 1.1 christos isc_boolean_t disallowed = ISC_FALSE; 802 1.1 christos isc_result_t result = ISC_R_SUCCESS; 803 1.1 christos isc_event_t *event; 804 1.1 christos 805 1.1 christos /* 806 1.1 christos * Send a shutdown event with action 'action' and argument 'arg' when 807 1.1 christos * 'task' is shutdown. 808 1.1 christos */ 809 1.1 christos 810 1.1 christos REQUIRE(VALID_TASK(task)); 811 1.1 christos REQUIRE(action != NULL); 812 1.1 christos 813 1.1 christos event = isc_event_allocate(task->manager->mctx, 814 1.1 christos NULL, 815 1.1 christos ISC_TASKEVENT_SHUTDOWN, 816 1.1 christos action, 817 1.1 christos arg, 818 1.1 christos sizeof(*event)); 819 1.1 christos if (event == NULL) 820 1.1 christos return (ISC_R_NOMEMORY); 821 1.1 christos 822 1.1 christos LOCK(&task->lock); 823 1.1 christos if (TASK_SHUTTINGDOWN(task)) { 824 1.1 christos disallowed = ISC_TRUE; 825 1.1 christos result = ISC_R_SHUTTINGDOWN; 826 1.1 christos } else 827 1.1 christos ENQUEUE(task->on_shutdown, event, ev_link); 828 1.1 christos UNLOCK(&task->lock); 829 1.1 christos 830 1.1 christos if (disallowed) 831 1.1 christos isc_mem_put(task->manager->mctx, event, sizeof(*event)); 832 1.1 christos 833 1.1 christos return (result); 834 1.1 christos } 835 1.1 christos 836 1.1 christos ISC_TASKFUNC_SCOPE void 837 1.1 christos isc__task_shutdown(isc_task_t *task0) { 838 1.1 christos isc__task_t *task = (isc__task_t *)task0; 839 1.1 christos isc_boolean_t was_idle; 840 1.1 christos 841 1.1 christos /* 842 1.1 christos * Shutdown 'task'. 843 1.1 christos */ 844 1.1 christos 845 1.1 christos REQUIRE(VALID_TASK(task)); 846 1.1 christos 847 1.1 christos LOCK(&task->lock); 848 1.1 christos was_idle = task_shutdown(task); 849 1.1 christos UNLOCK(&task->lock); 850 1.1 christos 851 1.1 christos if (was_idle) 852 1.1 christos task_ready(task); 853 1.1 christos } 854 1.1 christos 855 1.1 christos ISC_TASKFUNC_SCOPE void 856 1.1 christos isc__task_destroy(isc_task_t **taskp) { 857 1.1 christos 858 1.1 christos /* 859 1.1 christos * Destroy '*taskp'. 860 1.1 christos */ 861 1.1 christos 862 1.1 christos REQUIRE(taskp != NULL); 863 1.1 christos 864 1.1 christos isc_task_shutdown(*taskp); 865 1.1 christos isc_task_detach(taskp); 866 1.1 christos } 867 1.1 christos 868 1.1 christos ISC_TASKFUNC_SCOPE void 869 1.1 christos isc__task_setname(isc_task_t *task0, const char *name, void *tag) { 870 1.1 christos isc__task_t *task = (isc__task_t *)task0; 871 1.1 christos 872 1.1 christos /* 873 1.1 christos * Name 'task'. 874 1.1 christos */ 875 1.1 christos 876 1.1 christos REQUIRE(VALID_TASK(task)); 877 1.1 christos 878 1.1 christos LOCK(&task->lock); 879 1.1 christos memset(task->name, 0, sizeof(task->name)); 880 1.1 christos strncpy(task->name, name, sizeof(task->name) - 1); 881 1.1 christos task->tag = tag; 882 1.1 christos UNLOCK(&task->lock); 883 1.1 christos } 884 1.1 christos 885 1.1 christos ISC_TASKFUNC_SCOPE const char * 886 1.1 christos isc__task_getname(isc_task_t *task0) { 887 1.1 christos isc__task_t *task = (isc__task_t *)task0; 888 1.1 christos 889 1.1 christos REQUIRE(VALID_TASK(task)); 890 1.1 christos 891 1.1 christos return (task->name); 892 1.1 christos } 893 1.1 christos 894 1.1 christos ISC_TASKFUNC_SCOPE void * 895 1.1 christos isc__task_gettag(isc_task_t *task0) { 896 1.1 christos isc__task_t *task = (isc__task_t *)task0; 897 1.1 christos 898 1.1 christos REQUIRE(VALID_TASK(task)); 899 1.1 christos 900 1.1 christos return (task->tag); 901 1.1 christos } 902 1.1 christos 903 1.1 christos ISC_TASKFUNC_SCOPE void 904 1.1 christos isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { 905 1.1 christos isc__task_t *task = (isc__task_t *)task0; 906 1.1 christos 907 1.1 christos REQUIRE(VALID_TASK(task)); 908 1.1 christos REQUIRE(t != NULL); 909 1.1 christos 910 1.1 christos LOCK(&task->lock); 911 1.1 christos *t = task->now; 912 1.1 christos UNLOCK(&task->lock); 913 1.1 christos } 914 1.1 christos 915 1.1 christos /*** 916 1.1 christos *** Task Manager. 917 1.1 christos ***/ 918 1.1 christos 919 1.1 christos /* 920 1.1 christos * Return ISC_TRUE if the current ready list for the manager, which is 921 1.1 christos * either ready_tasks or the ready_priority_tasks, depending on whether 922 1.1 christos * the manager is currently in normal or privileged execution mode. 923 1.1 christos * 924 1.1 christos * Caller must hold the task manager lock. 925 1.1 christos */ 926 1.1 christos static inline isc_boolean_t 927 1.1 christos empty_readyq(isc__taskmgr_t *manager) { 928 1.1 christos isc__tasklist_t queue; 929 1.1 christos 930 1.1 christos if (manager->mode == isc_taskmgrmode_normal) 931 1.1 christos queue = manager->ready_tasks; 932 1.1 christos else 933 1.1 christos queue = manager->ready_priority_tasks; 934 1.1 christos 935 1.1 christos return (ISC_TF(EMPTY(queue))); 936 1.1 christos } 937 1.1 christos 938 1.1 christos /* 939 1.1 christos * Dequeue and return a pointer to the first task on the current ready 940 1.1 christos * list for the manager. 941 1.1 christos * If the task is privileged, dequeue it from the other ready list 942 1.1 christos * as well. 943 1.1 christos * 944 1.1 christos * Caller must hold the task manager lock. 945 1.1 christos */ 946 1.1 christos static inline isc__task_t * 947 1.1 christos pop_readyq(isc__taskmgr_t *manager) { 948 1.1 christos isc__task_t *task; 949 1.1 christos 950 1.1 christos if (manager->mode == isc_taskmgrmode_normal) 951 1.1 christos task = HEAD(manager->ready_tasks); 952 1.1 christos else 953 1.1 christos task = HEAD(manager->ready_priority_tasks); 954 1.1 christos 955 1.1 christos if (task != NULL) { 956 1.1 christos DEQUEUE(manager->ready_tasks, task, ready_link); 957 1.1 christos if (ISC_LINK_LINKED(task, ready_priority_link)) 958 1.1 christos DEQUEUE(manager->ready_priority_tasks, task, 959 1.1 christos ready_priority_link); 960 1.1 christos } 961 1.1 christos 962 1.1 christos return (task); 963 1.1 christos } 964 1.1 christos 965 1.1 christos /* 966 1.1 christos * Push 'task' onto the ready_tasks queue. If 'task' has the privilege 967 1.1 christos * flag set, then also push it onto the ready_priority_tasks queue. 968 1.1 christos * 969 1.1 christos * Caller must hold the task manager lock. 970 1.1 christos */ 971 1.1 christos static inline void 972 1.1 christos push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { 973 1.1 christos ENQUEUE(manager->ready_tasks, task, ready_link); 974 1.1 christos if ((task->flags & TASK_F_PRIVILEGED) != 0) 975 1.1 christos ENQUEUE(manager->ready_priority_tasks, task, 976 1.1 christos ready_priority_link); 977 1.1 christos } 978 1.1 christos 979 1.1 christos static void 980 1.1 christos dispatch(isc__taskmgr_t *manager) { 981 1.1 christos isc__task_t *task; 982 1.1 christos #ifndef USE_WORKER_THREADS 983 1.1 christos unsigned int total_dispatch_count = 0; 984 1.1 christos isc__tasklist_t new_ready_tasks; 985 1.1 christos isc__tasklist_t new_priority_tasks; 986 1.1 christos #endif /* USE_WORKER_THREADS */ 987 1.1 christos 988 1.1 christos REQUIRE(VALID_MANAGER(manager)); 989 1.1 christos 990 1.1 christos /* 991 1.1 christos * Again we're trying to hold the lock for as short a time as possible 992 1.1 christos * and to do as little locking and unlocking as possible. 993 1.1 christos * 994 1.1 christos * In both while loops, the appropriate lock must be held before the 995 1.1 christos * while body starts. Code which acquired the lock at the top of 996 1.1 christos * the loop would be more readable, but would result in a lot of 997 1.1 christos * extra locking. Compare: 998 1.1 christos * 999 1.1 christos * Straightforward: 1000 1.1 christos * 1001 1.1 christos * LOCK(); 1002 1.1 christos * ... 1003 1.1 christos * UNLOCK(); 1004 1.1 christos * while (expression) { 1005 1.1 christos * LOCK(); 1006 1.1 christos * ... 1007 1.1 christos * UNLOCK(); 1008 1.1 christos * 1009 1.1 christos * Unlocked part here... 1010 1.1 christos * 1011 1.1 christos * LOCK(); 1012 1.1 christos * ... 1013 1.1 christos * UNLOCK(); 1014 1.1 christos * } 1015 1.1 christos * 1016 1.1 christos * Note how if the loop continues we unlock and then immediately lock. 1017 1.1 christos * For N iterations of the loop, this code does 2N+1 locks and 2N+1 1018 1.1 christos * unlocks. Also note that the lock is not held when the while 1019 1.1 christos * condition is tested, which may or may not be important, depending 1020 1.1 christos * on the expression. 1021 1.1 christos * 1022 1.1 christos * As written: 1023 1.1 christos * 1024 1.1 christos * LOCK(); 1025 1.1 christos * while (expression) { 1026 1.1 christos * ... 1027 1.1 christos * UNLOCK(); 1028 1.1 christos * 1029 1.1 christos * Unlocked part here... 1030 1.1 christos * 1031 1.1 christos * LOCK(); 1032 1.1 christos * ... 1033 1.1 christos * } 1034 1.1 christos * UNLOCK(); 1035 1.1 christos * 1036 1.1 christos * For N iterations of the loop, this code does N+1 locks and N+1 1037 1.1 christos * unlocks. The while expression is always protected by the lock. 1038 1.1 christos */ 1039 1.1 christos 1040 1.1 christos #ifndef USE_WORKER_THREADS 1041 1.1 christos ISC_LIST_INIT(new_ready_tasks); 1042 1.1 christos ISC_LIST_INIT(new_priority_tasks); 1043 1.1 christos #endif 1044 1.1 christos LOCK(&manager->lock); 1045 1.1 christos 1046 1.1 christos while (!FINISHED(manager)) { 1047 1.1 christos #ifdef USE_WORKER_THREADS 1048 1.1 christos /* 1049 1.1 christos * For reasons similar to those given in the comment in 1050 1.1 christos * isc_task_send() above, it is safe for us to dequeue 1051 1.1 christos * the task while only holding the manager lock, and then 1052 1.1 christos * change the task to running state while only holding the 1053 1.1 christos * task lock. 1054 1.1 christos * 1055 1.1 christos * If a pause has been requested, don't do any work 1056 1.1 christos * until it's been released. 1057 1.1 christos */ 1058 1.1 christos while ((empty_readyq(manager) || manager->pause_requested || 1059 1.1 christos manager->exclusive_requested) && !FINISHED(manager)) 1060 1.1 christos { 1061 1.1 christos XTHREADTRACE(isc_msgcat_get(isc_msgcat, 1062 1.1 christos ISC_MSGSET_GENERAL, 1063 1.1 christos ISC_MSG_WAIT, "wait")); 1064 1.1 christos WAIT(&manager->work_available, &manager->lock); 1065 1.1 christos XTHREADTRACE(isc_msgcat_get(isc_msgcat, 1066 1.1 christos ISC_MSGSET_TASK, 1067 1.1 christos ISC_MSG_AWAKE, "awake")); 1068 1.1 christos } 1069 1.1 christos #else /* USE_WORKER_THREADS */ 1070 1.1 christos if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || 1071 1.1 christos empty_readyq(manager)) 1072 1.1 christos break; 1073 1.1 christos #endif /* USE_WORKER_THREADS */ 1074 1.1 christos XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, 1075 1.1 christos ISC_MSG_WORKING, "working")); 1076 1.1 christos 1077 1.1 christos task = pop_readyq(manager); 1078 1.1 christos if (task != NULL) { 1079 1.1 christos unsigned int dispatch_count = 0; 1080 1.1 christos isc_boolean_t done = ISC_FALSE; 1081 1.1 christos isc_boolean_t requeue = ISC_FALSE; 1082 1.1 christos isc_boolean_t finished = ISC_FALSE; 1083 1.1 christos isc_event_t *event; 1084 1.1 christos 1085 1.1 christos INSIST(VALID_TASK(task)); 1086 1.1 christos 1087 1.1 christos /* 1088 1.1 christos * Note we only unlock the manager lock if we actually 1089 1.1 christos * have a task to do. We must reacquire the manager 1090 1.1 christos * lock before exiting the 'if (task != NULL)' block. 1091 1.1 christos */ 1092 1.1 christos manager->tasks_running++; 1093 1.1 christos UNLOCK(&manager->lock); 1094 1.1 christos 1095 1.1 christos LOCK(&task->lock); 1096 1.1 christos INSIST(task->state == task_state_ready); 1097 1.1 christos task->state = task_state_running; 1098 1.1 christos XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1099 1.1 christos ISC_MSG_RUNNING, "running")); 1100 1.1 christos isc_stdtime_get(&task->now); 1101 1.1 christos do { 1102 1.1 christos if (!EMPTY(task->events)) { 1103 1.1 christos event = HEAD(task->events); 1104 1.1 christos DEQUEUE(task->events, event, ev_link); 1105 1.1 christos 1106 1.1 christos /* 1107 1.1 christos * Execute the event action. 1108 1.1 christos */ 1109 1.1 christos XTRACE(isc_msgcat_get(isc_msgcat, 1110 1.1 christos ISC_MSGSET_TASK, 1111 1.1 christos ISC_MSG_EXECUTE, 1112 1.1 christos "execute action")); 1113 1.1 christos if (event->ev_action != NULL) { 1114 1.1 christos UNLOCK(&task->lock); 1115 1.1 christos (event->ev_action)( 1116 1.1 christos (isc_task_t *)task, 1117 1.1 christos event); 1118 1.1 christos LOCK(&task->lock); 1119 1.1 christos } 1120 1.1 christos dispatch_count++; 1121 1.1 christos #ifndef USE_WORKER_THREADS 1122 1.1 christos total_dispatch_count++; 1123 1.1 christos #endif /* USE_WORKER_THREADS */ 1124 1.1 christos } 1125 1.1 christos 1126 1.1 christos if (task->references == 0 && 1127 1.1 christos EMPTY(task->events) && 1128 1.1 christos !TASK_SHUTTINGDOWN(task)) { 1129 1.1 christos isc_boolean_t was_idle; 1130 1.1 christos 1131 1.1 christos /* 1132 1.1 christos * There are no references and no 1133 1.1 christos * pending events for this task, 1134 1.1 christos * which means it will not become 1135 1.1 christos * runnable again via an external 1136 1.1 christos * action (such as sending an event 1137 1.1 christos * or detaching). 1138 1.1 christos * 1139 1.1 christos * We initiate shutdown to prevent 1140 1.1 christos * it from becoming a zombie. 1141 1.1 christos * 1142 1.1 christos * We do this here instead of in 1143 1.1 christos * the "if EMPTY(task->events)" block 1144 1.1 christos * below because: 1145 1.1 christos * 1146 1.1 christos * If we post no shutdown events, 1147 1.1 christos * we want the task to finish. 1148 1.1 christos * 1149 1.1 christos * If we did post shutdown events, 1150 1.1 christos * will still want the task's 1151 1.1 christos * quantum to be applied. 1152 1.1 christos */ 1153 1.1 christos was_idle = task_shutdown(task); 1154 1.1 christos INSIST(!was_idle); 1155 1.1 christos } 1156 1.1 christos 1157 1.1 christos if (EMPTY(task->events)) { 1158 1.1 christos /* 1159 1.1 christos * Nothing else to do for this task 1160 1.1 christos * right now. 1161 1.1 christos */ 1162 1.1 christos XTRACE(isc_msgcat_get(isc_msgcat, 1163 1.1 christos ISC_MSGSET_TASK, 1164 1.1 christos ISC_MSG_EMPTY, 1165 1.1 christos "empty")); 1166 1.1 christos if (task->references == 0 && 1167 1.1 christos TASK_SHUTTINGDOWN(task)) { 1168 1.1 christos /* 1169 1.1 christos * The task is done. 1170 1.1 christos */ 1171 1.1 christos XTRACE(isc_msgcat_get( 1172 1.1 christos isc_msgcat, 1173 1.1 christos ISC_MSGSET_TASK, 1174 1.1 christos ISC_MSG_DONE, 1175 1.1 christos "done")); 1176 1.1 christos finished = ISC_TRUE; 1177 1.1 christos task->state = task_state_done; 1178 1.1 christos } else 1179 1.1 christos task->state = task_state_idle; 1180 1.1 christos done = ISC_TRUE; 1181 1.1 christos } else if (dispatch_count >= task->quantum) { 1182 1.1 christos /* 1183 1.1 christos * Our quantum has expired, but 1184 1.1 christos * there is more work to be done. 1185 1.1 christos * We'll requeue it to the ready 1186 1.1 christos * queue later. 1187 1.1 christos * 1188 1.1 christos * We don't check quantum until 1189 1.1 christos * dispatching at least one event, 1190 1.1 christos * so the minimum quantum is one. 1191 1.1 christos */ 1192 1.1 christos XTRACE(isc_msgcat_get(isc_msgcat, 1193 1.1 christos ISC_MSGSET_TASK, 1194 1.1 christos ISC_MSG_QUANTUM, 1195 1.1 christos "quantum")); 1196 1.1 christos task->state = task_state_ready; 1197 1.1 christos requeue = ISC_TRUE; 1198 1.1 christos done = ISC_TRUE; 1199 1.1 christos } 1200 1.1 christos } while (!done); 1201 1.1 christos UNLOCK(&task->lock); 1202 1.1 christos 1203 1.1 christos if (finished) 1204 1.1 christos task_finished(task); 1205 1.1 christos 1206 1.1 christos LOCK(&manager->lock); 1207 1.1 christos manager->tasks_running--; 1208 1.1 christos #ifdef USE_WORKER_THREADS 1209 1.1 christos if (manager->exclusive_requested && 1210 1.1 christos manager->tasks_running == 1) { 1211 1.1 christos SIGNAL(&manager->exclusive_granted); 1212 1.1 christos } else if (manager->pause_requested && 1213 1.1 christos manager->tasks_running == 0) { 1214 1.1 christos SIGNAL(&manager->paused); 1215 1.1 christos } 1216 1.1 christos #endif /* USE_WORKER_THREADS */ 1217 1.1 christos if (requeue) { 1218 1.1 christos /* 1219 1.1 christos * We know we're awake, so we don't have 1220 1.1 christos * to wakeup any sleeping threads if the 1221 1.1 christos * ready queue is empty before we requeue. 1222 1.1 christos * 1223 1.1 christos * A possible optimization if the queue is 1224 1.1 christos * empty is to 'goto' the 'if (task != NULL)' 1225 1.1 christos * block, avoiding the ENQUEUE of the task 1226 1.1 christos * and the subsequent immediate DEQUEUE 1227 1.1 christos * (since it is the only executable task). 1228 1.1 christos * We don't do this because then we'd be 1229 1.1 christos * skipping the exit_requested check. The 1230 1.1 christos * cost of ENQUEUE is low anyway, especially 1231 1.1 christos * when you consider that we'd have to do 1232 1.1 christos * an extra EMPTY check to see if we could 1233 1.1 christos * do the optimization. If the ready queue 1234 1.1 christos * were usually nonempty, the 'optimization' 1235 1.1 christos * might even hurt rather than help. 1236 1.1 christos */ 1237 1.1 christos #ifdef USE_WORKER_THREADS 1238 1.1 christos push_readyq(manager, task); 1239 1.1 christos #else 1240 1.1 christos ENQUEUE(new_ready_tasks, task, ready_link); 1241 1.1 christos if ((task->flags & TASK_F_PRIVILEGED) != 0) 1242 1.1 christos ENQUEUE(new_priority_tasks, task, 1243 1.1 christos ready_priority_link); 1244 1.1 christos #endif 1245 1.1 christos } 1246 1.1 christos } 1247 1.1 christos 1248 1.1 christos #ifdef USE_WORKER_THREADS 1249 1.1 christos /* 1250 1.1 christos * If we are in privileged execution mode and there are no 1251 1.1 christos * tasks remaining on the current ready queue, then 1252 1.1 christos * we're stuck. Automatically drop privileges at that 1253 1.1 christos * point and continue with the regular ready queue. 1254 1.1 christos */ 1255 1.1 christos if (manager->tasks_running == 0 && empty_readyq(manager)) { 1256 1.1 christos manager->mode = isc_taskmgrmode_normal; 1257 1.1 christos if (!empty_readyq(manager)) 1258 1.1 christos BROADCAST(&manager->work_available); 1259 1.1 christos } 1260 1.1 christos #endif 1261 1.1 christos } 1262 1.1 christos 1263 1.1 christos #ifndef USE_WORKER_THREADS 1264 1.1 christos ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); 1265 1.1 christos ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, 1266 1.1 christos ready_priority_link); 1267 1.1 christos if (empty_readyq(manager)) 1268 1.1 christos manager->mode = isc_taskmgrmode_normal; 1269 1.1 christos #endif 1270 1.1 christos 1271 1.1 christos UNLOCK(&manager->lock); 1272 1.1 christos } 1273 1.1 christos 1274 1.1 christos #ifdef USE_WORKER_THREADS 1275 1.1 christos static isc_threadresult_t 1276 1.1 christos #ifdef _WIN32 1277 1.1 christos WINAPI 1278 1.1 christos #endif 1279 1.1 christos run(void *uap) { 1280 1.1 christos isc__taskmgr_t *manager = uap; 1281 1.1 christos 1282 1.1 christos XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1283 1.1 christos ISC_MSG_STARTING, "starting")); 1284 1.1 christos 1285 1.1 christos dispatch(manager); 1286 1.1 christos 1287 1.1 christos XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1288 1.1 christos ISC_MSG_EXITING, "exiting")); 1289 1.1 christos 1290 1.1 christos #ifdef OPENSSL_LEAKS 1291 1.1 christos ERR_remove_state(0); 1292 1.1 christos #endif 1293 1.1 christos 1294 1.1 christos return ((isc_threadresult_t)0); 1295 1.1 christos } 1296 1.1 christos #endif /* USE_WORKER_THREADS */ 1297 1.1 christos 1298 1.1 christos static void 1299 1.1 christos manager_free(isc__taskmgr_t *manager) { 1300 1.1 christos isc_mem_t *mctx; 1301 1.1 christos 1302 1.1 christos LOCK(&manager->lock); 1303 1.1 christos #ifdef USE_WORKER_THREADS 1304 1.1 christos (void)isc_condition_destroy(&manager->exclusive_granted); 1305 1.1 christos (void)isc_condition_destroy(&manager->work_available); 1306 1.1 christos (void)isc_condition_destroy(&manager->paused); 1307 1.1 christos isc_mem_free(manager->mctx, manager->threads); 1308 1.1 christos #endif /* USE_WORKER_THREADS */ 1309 1.1 christos manager->common.impmagic = 0; 1310 1.1 christos manager->common.magic = 0; 1311 1.1 christos mctx = manager->mctx; 1312 1.1 christos UNLOCK(&manager->lock); 1313 1.1 christos DESTROYLOCK(&manager->lock); 1314 1.1 christos isc_mem_put(mctx, manager, sizeof(*manager)); 1315 1.1 christos isc_mem_detach(&mctx); 1316 1.1 christos 1317 1.1 christos #ifdef USE_SHARED_MANAGER 1318 1.1 christos taskmgr = NULL; 1319 1.1 christos #endif /* USE_SHARED_MANAGER */ 1320 1.1 christos } 1321 1.1 christos 1322 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 1323 1.1 christos isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, 1324 1.1 christos unsigned int default_quantum, isc_taskmgr_t **managerp) 1325 1.1 christos { 1326 1.1 christos isc_result_t result; 1327 1.1 christos unsigned int i, started = 0; 1328 1.1 christos isc__taskmgr_t *manager; 1329 1.1 christos 1330 1.1 christos /* 1331 1.1 christos * Create a new task manager. 1332 1.1 christos */ 1333 1.1 christos 1334 1.1 christos REQUIRE(workers > 0); 1335 1.1 christos REQUIRE(managerp != NULL && *managerp == NULL); 1336 1.1 christos 1337 1.1 christos #ifndef USE_WORKER_THREADS 1338 1.1 christos UNUSED(i); 1339 1.1 christos UNUSED(started); 1340 1.1 christos #endif 1341 1.1 christos 1342 1.1 christos #ifdef USE_SHARED_MANAGER 1343 1.1 christos if (taskmgr != NULL) { 1344 1.1 christos if (taskmgr->refs == 0) 1345 1.1 christos return (ISC_R_SHUTTINGDOWN); 1346 1.1 christos taskmgr->refs++; 1347 1.1 christos *managerp = (isc_taskmgr_t *)taskmgr; 1348 1.1 christos return (ISC_R_SUCCESS); 1349 1.1 christos } 1350 1.1 christos #endif /* USE_SHARED_MANAGER */ 1351 1.1 christos 1352 1.1 christos manager = isc_mem_get(mctx, sizeof(*manager)); 1353 1.1 christos if (manager == NULL) 1354 1.1 christos return (ISC_R_NOMEMORY); 1355 1.1 christos manager->common.methods = &taskmgrmethods; 1356 1.1 christos manager->common.impmagic = TASK_MANAGER_MAGIC; 1357 1.1 christos manager->common.magic = ISCAPI_TASKMGR_MAGIC; 1358 1.1 christos manager->mode = isc_taskmgrmode_normal; 1359 1.1 christos manager->mctx = NULL; 1360 1.1 christos result = isc_mutex_init(&manager->lock); 1361 1.1 christos if (result != ISC_R_SUCCESS) 1362 1.1 christos goto cleanup_mgr; 1363 1.1 christos LOCK(&manager->lock); 1364 1.1 christos 1365 1.1 christos #ifdef USE_WORKER_THREADS 1366 1.1 christos manager->workers = 0; 1367 1.1 christos manager->threads = isc_mem_allocate(mctx, 1368 1.1 christos workers * sizeof(isc_thread_t)); 1369 1.1 christos if (manager->threads == NULL) { 1370 1.1 christos result = ISC_R_NOMEMORY; 1371 1.1 christos goto cleanup_lock; 1372 1.1 christos } 1373 1.1 christos if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) { 1374 1.1 christos UNEXPECTED_ERROR(__FILE__, __LINE__, 1375 1.1 christos "isc_condition_init() %s", 1376 1.1 christos isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1377 1.1 christos ISC_MSG_FAILED, "failed")); 1378 1.1 christos result = ISC_R_UNEXPECTED; 1379 1.1 christos goto cleanup_threads; 1380 1.1 christos } 1381 1.1 christos if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) { 1382 1.1 christos UNEXPECTED_ERROR(__FILE__, __LINE__, 1383 1.1 christos "isc_condition_init() %s", 1384 1.1 christos isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1385 1.1 christos ISC_MSG_FAILED, "failed")); 1386 1.1 christos result = ISC_R_UNEXPECTED; 1387 1.1 christos goto cleanup_workavailable; 1388 1.1 christos } 1389 1.1 christos if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS) { 1390 1.1 christos UNEXPECTED_ERROR(__FILE__, __LINE__, 1391 1.1 christos "isc_condition_init() %s", 1392 1.1 christos isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, 1393 1.1 christos ISC_MSG_FAILED, "failed")); 1394 1.1 christos result = ISC_R_UNEXPECTED; 1395 1.1 christos goto cleanup_exclusivegranted; 1396 1.1 christos } 1397 1.1 christos #endif /* USE_WORKER_THREADS */ 1398 1.1 christos if (default_quantum == 0) 1399 1.1 christos default_quantum = DEFAULT_DEFAULT_QUANTUM; 1400 1.1 christos manager->default_quantum = default_quantum; 1401 1.1 christos INIT_LIST(manager->tasks); 1402 1.1 christos INIT_LIST(manager->ready_tasks); 1403 1.1 christos INIT_LIST(manager->ready_priority_tasks); 1404 1.1 christos manager->tasks_running = 0; 1405 1.1 christos manager->exclusive_requested = ISC_FALSE; 1406 1.1 christos manager->pause_requested = ISC_FALSE; 1407 1.1 christos manager->exiting = ISC_FALSE; 1408 1.1 christos 1409 1.1 christos isc_mem_attach(mctx, &manager->mctx); 1410 1.1 christos 1411 1.1 christos #ifdef USE_WORKER_THREADS 1412 1.1 christos /* 1413 1.1 christos * Start workers. 1414 1.1 christos */ 1415 1.1 christos for (i = 0; i < workers; i++) { 1416 1.1 christos if (isc_thread_create(run, manager, 1417 1.1 christos &manager->threads[manager->workers]) == 1418 1.1 christos ISC_R_SUCCESS) { 1419 1.1 christos manager->workers++; 1420 1.1 christos started++; 1421 1.1 christos } 1422 1.1 christos } 1423 1.1 christos UNLOCK(&manager->lock); 1424 1.1 christos 1425 1.1 christos if (started == 0) { 1426 1.1 christos manager_free(manager); 1427 1.1 christos return (ISC_R_NOTHREADS); 1428 1.1 christos } 1429 1.1 christos isc_thread_setconcurrency(workers); 1430 1.1 christos #endif /* USE_WORKER_THREADS */ 1431 1.1 christos #ifdef USE_SHARED_MANAGER 1432 1.1 christos manager->refs = 1; 1433 1.1 christos UNLOCK(&manager->lock); 1434 1.1 christos taskmgr = manager; 1435 1.1 christos #endif /* USE_SHARED_MANAGER */ 1436 1.1 christos 1437 1.1 christos *managerp = (isc_taskmgr_t *)manager; 1438 1.1 christos 1439 1.1 christos return (ISC_R_SUCCESS); 1440 1.1 christos 1441 1.1 christos #ifdef USE_WORKER_THREADS 1442 1.1 christos cleanup_exclusivegranted: 1443 1.1 christos (void)isc_condition_destroy(&manager->exclusive_granted); 1444 1.1 christos cleanup_workavailable: 1445 1.1 christos (void)isc_condition_destroy(&manager->work_available); 1446 1.1 christos cleanup_threads: 1447 1.1 christos isc_mem_free(mctx, manager->threads); 1448 1.1 christos cleanup_lock: 1449 1.1 christos UNLOCK(&manager->lock); 1450 1.1 christos DESTROYLOCK(&manager->lock); 1451 1.1 christos #endif 1452 1.1 christos cleanup_mgr: 1453 1.1 christos isc_mem_put(mctx, manager, sizeof(*manager)); 1454 1.1 christos return (result); 1455 1.1 christos } 1456 1.1 christos 1457 1.1 christos ISC_TASKFUNC_SCOPE void 1458 1.1 christos isc__taskmgr_destroy(isc_taskmgr_t **managerp) { 1459 1.1 christos isc__taskmgr_t *manager; 1460 1.1 christos isc__task_t *task; 1461 1.1 christos unsigned int i; 1462 1.1 christos 1463 1.1 christos /* 1464 1.1 christos * Destroy '*managerp'. 1465 1.1 christos */ 1466 1.1 christos 1467 1.1 christos REQUIRE(managerp != NULL); 1468 1.1 christos manager = (void*)(*managerp); 1469 1.1 christos REQUIRE(VALID_MANAGER(manager)); 1470 1.1 christos 1471 1.1 christos #ifndef USE_WORKER_THREADS 1472 1.1 christos UNUSED(i); 1473 1.1 christos #endif /* USE_WORKER_THREADS */ 1474 1.1 christos 1475 1.1 christos #ifdef USE_SHARED_MANAGER 1476 1.1 christos manager->refs--; 1477 1.1 christos if (manager->refs > 0) { 1478 1.1 christos *managerp = NULL; 1479 1.1 christos return; 1480 1.1 christos } 1481 1.1 christos #endif 1482 1.1 christos 1483 1.1 christos XTHREADTRACE("isc_taskmgr_destroy"); 1484 1.1 christos /* 1485 1.1 christos * Only one non-worker thread may ever call this routine. 1486 1.1 christos * If a worker thread wants to initiate shutdown of the 1487 1.1 christos * task manager, it should ask some non-worker thread to call 1488 1.1 christos * isc_taskmgr_destroy(), e.g. by signalling a condition variable 1489 1.1 christos * that the startup thread is sleeping on. 1490 1.1 christos */ 1491 1.1 christos 1492 1.1 christos /* 1493 1.1 christos * Unlike elsewhere, we're going to hold this lock a long time. 1494 1.1 christos * We need to do so, because otherwise the list of tasks could 1495 1.1 christos * change while we were traversing it. 1496 1.1 christos * 1497 1.1 christos * This is also the only function where we will hold both the 1498 1.1 christos * task manager lock and a task lock at the same time. 1499 1.1 christos */ 1500 1.1 christos 1501 1.1 christos LOCK(&manager->lock); 1502 1.1 christos 1503 1.1 christos /* 1504 1.1 christos * Make sure we only get called once. 1505 1.1 christos */ 1506 1.1 christos INSIST(!manager->exiting); 1507 1.1 christos manager->exiting = ISC_TRUE; 1508 1.1 christos 1509 1.1 christos /* 1510 1.1 christos * If privileged mode was on, turn it off. 1511 1.1 christos */ 1512 1.1 christos manager->mode = isc_taskmgrmode_normal; 1513 1.1 christos 1514 1.1 christos /* 1515 1.1 christos * Post shutdown event(s) to every task (if they haven't already been 1516 1.1 christos * posted). 1517 1.1 christos */ 1518 1.1 christos for (task = HEAD(manager->tasks); 1519 1.1 christos task != NULL; 1520 1.1 christos task = NEXT(task, link)) { 1521 1.1 christos LOCK(&task->lock); 1522 1.1 christos if (task_shutdown(task)) 1523 1.1 christos push_readyq(manager, task); 1524 1.1 christos UNLOCK(&task->lock); 1525 1.1 christos } 1526 1.1 christos #ifdef USE_WORKER_THREADS 1527 1.1 christos /* 1528 1.1 christos * Wake up any sleeping workers. This ensures we get work done if 1529 1.1 christos * there's work left to do, and if there are already no tasks left 1530 1.1 christos * it will cause the workers to see manager->exiting. 1531 1.1 christos */ 1532 1.1 christos BROADCAST(&manager->work_available); 1533 1.1 christos UNLOCK(&manager->lock); 1534 1.1 christos 1535 1.1 christos /* 1536 1.1 christos * Wait for all the worker threads to exit. 1537 1.1 christos */ 1538 1.1 christos for (i = 0; i < manager->workers; i++) 1539 1.1 christos (void)isc_thread_join(manager->threads[i], NULL); 1540 1.1 christos #else /* USE_WORKER_THREADS */ 1541 1.1 christos /* 1542 1.1 christos * Dispatch the shutdown events. 1543 1.1 christos */ 1544 1.1 christos UNLOCK(&manager->lock); 1545 1.1 christos while (isc__taskmgr_ready((isc_taskmgr_t *)manager)) 1546 1.1 christos (void)isc__taskmgr_dispatch((isc_taskmgr_t *)manager); 1547 1.1 christos #ifdef BIND9 1548 1.1 christos if (!ISC_LIST_EMPTY(manager->tasks)) 1549 1.1 christos isc_mem_printallactive(stderr); 1550 1.1 christos #endif 1551 1.1 christos INSIST(ISC_LIST_EMPTY(manager->tasks)); 1552 1.1 christos #ifdef USE_SHARED_MANAGER 1553 1.1 christos taskmgr = NULL; 1554 1.1 christos #endif 1555 1.1 christos #endif /* USE_WORKER_THREADS */ 1556 1.1 christos 1557 1.1 christos manager_free(manager); 1558 1.1 christos 1559 1.1 christos *managerp = NULL; 1560 1.1 christos } 1561 1.1 christos 1562 1.1 christos ISC_TASKFUNC_SCOPE void 1563 1.1 christos isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode) { 1564 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1565 1.1 christos 1566 1.1 christos LOCK(&manager->lock); 1567 1.1 christos manager->mode = mode; 1568 1.1 christos UNLOCK(&manager->lock); 1569 1.1 christos } 1570 1.1 christos 1571 1.1 christos ISC_TASKFUNC_SCOPE isc_taskmgrmode_t 1572 1.1 christos isc__taskmgr_mode(isc_taskmgr_t *manager0) { 1573 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1574 1.1 christos isc_taskmgrmode_t mode; 1575 1.1 christos LOCK(&manager->lock); 1576 1.1 christos mode = manager->mode; 1577 1.1 christos UNLOCK(&manager->lock); 1578 1.1 christos return (mode); 1579 1.1 christos } 1580 1.1 christos 1581 1.1 christos #ifndef USE_WORKER_THREADS 1582 1.1 christos isc_boolean_t 1583 1.1 christos isc__taskmgr_ready(isc_taskmgr_t *manager0) { 1584 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1585 1.1 christos isc_boolean_t is_ready; 1586 1.1 christos 1587 1.1 christos #ifdef USE_SHARED_MANAGER 1588 1.1 christos if (manager == NULL) 1589 1.1 christos manager = taskmgr; 1590 1.1 christos #endif 1591 1.1 christos if (manager == NULL) 1592 1.1 christos return (ISC_FALSE); 1593 1.1 christos 1594 1.1 christos LOCK(&manager->lock); 1595 1.1 christos is_ready = !empty_readyq(manager); 1596 1.1 christos UNLOCK(&manager->lock); 1597 1.1 christos 1598 1.1 christos return (is_ready); 1599 1.1 christos } 1600 1.1 christos 1601 1.1 christos isc_result_t 1602 1.1 christos isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { 1603 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1604 1.1 christos 1605 1.1 christos #ifdef USE_SHARED_MANAGER 1606 1.1 christos if (manager == NULL) 1607 1.1 christos manager = taskmgr; 1608 1.1 christos #endif 1609 1.1 christos if (manager == NULL) 1610 1.1 christos return (ISC_R_NOTFOUND); 1611 1.1 christos 1612 1.1 christos dispatch(manager); 1613 1.1 christos 1614 1.1 christos return (ISC_R_SUCCESS); 1615 1.1 christos } 1616 1.1 christos 1617 1.1 christos #else 1618 1.1 christos ISC_TASKFUNC_SCOPE void 1619 1.1 christos isc__taskmgr_pause(isc_taskmgr_t *manager0) { 1620 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1621 1.1 christos LOCK(&manager->lock); 1622 1.1 christos while (manager->tasks_running > 0) { 1623 1.1 christos WAIT(&manager->paused, &manager->lock); 1624 1.1 christos } 1625 1.1 christos manager->pause_requested = ISC_TRUE; 1626 1.1 christos UNLOCK(&manager->lock); 1627 1.1 christos } 1628 1.1 christos 1629 1.1 christos ISC_TASKFUNC_SCOPE void 1630 1.1 christos isc__taskmgr_resume(isc_taskmgr_t *manager0) { 1631 1.1 christos isc__taskmgr_t *manager = (void*)manager0; 1632 1.1 christos 1633 1.1 christos LOCK(&manager->lock); 1634 1.1 christos if (manager->pause_requested) { 1635 1.1 christos manager->pause_requested = ISC_FALSE; 1636 1.1 christos BROADCAST(&manager->work_available); 1637 1.1 christos } 1638 1.1 christos UNLOCK(&manager->lock); 1639 1.1 christos } 1640 1.1 christos #endif /* USE_WORKER_THREADS */ 1641 1.1 christos 1642 1.1 christos ISC_TASKFUNC_SCOPE isc_result_t 1643 1.1 christos isc__task_beginexclusive(isc_task_t *task0) { 1644 1.1 christos #ifdef USE_WORKER_THREADS 1645 1.1 christos isc__task_t *task = (isc__task_t *)task0; 1646 1.1 christos isc__taskmgr_t *manager = task->manager; 1647 1.1 christos REQUIRE(task->state == task_state_running); 1648 1.1 christos LOCK(&manager->lock); 1649 1.1 christos if (manager->exclusive_requested) { 1650 1.1 christos UNLOCK(&manager->lock); 1651 1.1 christos return (ISC_R_LOCKBUSY); 1652 1.1 christos } 1653 1.1 christos manager->exclusive_requested = ISC_TRUE; 1654 1.1 christos while (manager->tasks_running > 1) { 1655 1.1 christos WAIT(&manager->exclusive_granted, &manager->lock); 1656 1.1 christos } 1657 1.1 christos UNLOCK(&manager->lock); 1658 1.1 christos #else 1659 1.1 christos UNUSED(task0); 1660 1.1 christos #endif 1661 1.1 christos return (ISC_R_SUCCESS); 1662 1.1 christos } 1663 1.1 christos 1664 1.1 christos ISC_TASKFUNC_SCOPE void 1665 1.1 christos isc__task_endexclusive(isc_task_t *task0) { 1666 1.1 christos #ifdef USE_WORKER_THREADS 1667 1.1 christos isc__task_t *task = (isc__task_t *)task0; 1668 1.1 christos isc__taskmgr_t *manager = task->manager; 1669 1.1 christos 1670 1.1 christos REQUIRE(task->state == task_state_running); 1671 1.1 christos LOCK(&manager->lock); 1672 1.1 christos REQUIRE(manager->exclusive_requested); 1673 1.1 christos manager->exclusive_requested = ISC_FALSE; 1674 1.1 christos BROADCAST(&manager->work_available); 1675 1.1 christos UNLOCK(&manager->lock); 1676 1.1 christos #else 1677 1.1 christos UNUSED(task0); 1678 1.1 christos #endif 1679 1.1 christos } 1680 1.1 christos 1681 1.1 christos ISC_TASKFUNC_SCOPE void 1682 1.1 christos isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv) { 1683 1.1 christos isc__task_t *task = (isc__task_t *)task0; 1684 1.1 christos isc__taskmgr_t *manager = task->manager; 1685 1.1 christos isc_boolean_t oldpriv; 1686 1.1 christos 1687 1.1 christos LOCK(&task->lock); 1688 1.1 christos oldpriv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); 1689 1.1 christos if (priv) 1690 1.1 christos task->flags |= TASK_F_PRIVILEGED; 1691 1.1 christos else 1692 1.1 christos task->flags &= ~TASK_F_PRIVILEGED; 1693 1.1 christos UNLOCK(&task->lock); 1694 1.1 christos 1695 1.1 christos if (priv == oldpriv) 1696 1.1 christos return; 1697 1.1 christos 1698 1.1 christos LOCK(&manager->lock); 1699 1.1 christos if (priv && ISC_LINK_LINKED(task, ready_link)) 1700 1.1 christos ENQUEUE(manager->ready_priority_tasks, task, 1701 1.1 christos ready_priority_link); 1702 1.1 christos else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) 1703 1.1 christos DEQUEUE(manager->ready_priority_tasks, task, 1704 1.1 christos ready_priority_link); 1705 1.1 christos UNLOCK(&manager->lock); 1706 1.1 christos } 1707 1.1 christos 1708 1.1 christos ISC_TASKFUNC_SCOPE isc_boolean_t 1709 1.1 christos isc__task_privilege(isc_task_t *task0) { 1710 1.1 christos isc__task_t *task = (isc__task_t *)task0; 1711 1.1 christos isc_boolean_t priv; 1712 1.1 christos 1713 1.1 christos LOCK(&task->lock); 1714 1.1 christos priv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); 1715 1.1 christos UNLOCK(&task->lock); 1716 1.1 christos return (priv); 1717 1.1 christos } 1718 1.1 christos 1719 1.1 christos #ifdef USE_SOCKETIMPREGISTER 1720 1.1 christos isc_result_t 1721 1.1 christos isc__task_register() { 1722 1.1 christos return (isc_task_register(isc__taskmgr_create)); 1723 1.1 christos } 1724 1.1 christos #endif 1725 1.1 christos 1726 1.1 christos isc_boolean_t 1727 1.1 christos isc_task_exiting(isc_task_t *t) { 1728 1.1 christos isc__task_t *task = (isc__task_t *)t; 1729 1.1 christos 1730 1.1 christos REQUIRE(VALID_TASK(task)); 1731 1.1 christos return (TASK_SHUTTINGDOWN(task)); 1732 1.1 christos } 1733 1.1 christos 1734 1.1 christos 1735 1.1 christos #if defined(HAVE_LIBXML2) && defined(BIND9) 1736 1.1 christos void 1737 1.1 christos isc_taskmgr_renderxml(isc_taskmgr_t *mgr0, xmlTextWriterPtr writer) { 1738 1.1 christos isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0; 1739 1.1 christos isc__task_t *task; 1740 1.1 christos 1741 1.1 christos LOCK(&mgr->lock); 1742 1.1 christos 1743 1.1 christos /* 1744 1.1 christos * Write out the thread-model, and some details about each depending 1745 1.1 christos * on which type is enabled. 1746 1.1 christos */ 1747 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model"); 1748 1.1 christos #ifdef ISC_PLATFORM_USETHREADS 1749 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"); 1750 1.1 christos xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded"); 1751 1.1 christos xmlTextWriterEndElement(writer); /* type */ 1752 1.1 christos 1753 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "worker-threads"); 1754 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", mgr->workers); 1755 1.1 christos xmlTextWriterEndElement(writer); /* worker-threads */ 1756 1.1 christos #else /* ISC_PLATFORM_USETHREADS */ 1757 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"); 1758 1.1 christos xmlTextWriterWriteString(writer, ISC_XMLCHAR "non-threaded"); 1759 1.1 christos xmlTextWriterEndElement(writer); /* type */ 1760 1.1 christos 1761 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"); 1762 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", mgr->refs); 1763 1.1 christos xmlTextWriterEndElement(writer); /* references */ 1764 1.1 christos #endif /* ISC_PLATFORM_USETHREADS */ 1765 1.1 christos 1766 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum"); 1767 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", mgr->default_quantum); 1768 1.1 christos xmlTextWriterEndElement(writer); /* default-quantum */ 1769 1.1 christos 1770 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running"); 1771 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_running); 1772 1.1 christos xmlTextWriterEndElement(writer); /* tasks-running */ 1773 1.1 christos 1774 1.1 christos xmlTextWriterEndElement(writer); /* thread-model */ 1775 1.1 christos 1776 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks"); 1777 1.1 christos task = ISC_LIST_HEAD(mgr->tasks); 1778 1.1 christos while (task != NULL) { 1779 1.1 christos LOCK(&task->lock); 1780 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "task"); 1781 1.1 christos 1782 1.1 christos if (task->name[0] != 0) { 1783 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"); 1784 1.1 christos xmlTextWriterWriteFormatString(writer, "%s", 1785 1.1 christos task->name); 1786 1.1 christos xmlTextWriterEndElement(writer); /* name */ 1787 1.1 christos } 1788 1.1 christos 1789 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "references"); 1790 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", task->references); 1791 1.1 christos xmlTextWriterEndElement(writer); /* references */ 1792 1.1 christos 1793 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "id"); 1794 1.1 christos xmlTextWriterWriteFormatString(writer, "%p", task); 1795 1.1 christos xmlTextWriterEndElement(writer); /* id */ 1796 1.1 christos 1797 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "state"); 1798 1.1 christos xmlTextWriterWriteFormatString(writer, "%s", 1799 1.1 christos statenames[task->state]); 1800 1.1 christos xmlTextWriterEndElement(writer); /* state */ 1801 1.1 christos 1802 1.1 christos xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum"); 1803 1.1 christos xmlTextWriterWriteFormatString(writer, "%d", task->quantum); 1804 1.1 christos xmlTextWriterEndElement(writer); /* quantum */ 1805 1.1 christos 1806 1.1 christos xmlTextWriterEndElement(writer); 1807 1.1 christos 1808 1.1 christos UNLOCK(&task->lock); 1809 1.1 christos task = ISC_LIST_NEXT(task, link); 1810 1.1 christos } 1811 1.1 christos xmlTextWriterEndElement(writer); /* tasks */ 1812 1.1 christos 1813 1.1 christos UNLOCK(&mgr->lock); 1814 1.1 christos } 1815 1.1 christos #endif /* HAVE_LIBXML2 && BIND9 */ 1816