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