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