1 /* 2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 * PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /*! \file 18 * \author Principal Author: Bob Halley 19 */ 20 21 /* 22 * XXXRTH Need to document the states a task can be in, and the rules 23 * for changing states. 24 */ 25 26 #include <stdlib.h> 27 #include <string.h> 28 #include <time.h> 29 30 #include <isc/event.h> 31 #include <isc/task.h> 32 #include <isc/util.h> 33 34 #include "task_p.h" 35 36 /*** 37 *** Types. 38 ***/ 39 40 typedef enum { 41 task_state_idle, task_state_ready, task_state_running, 42 task_state_done 43 } task_state_t; 44 45 struct isc_task { 46 /* Not locked. */ 47 isc_taskmgr_t * manager; 48 /* Locked by task lock. */ 49 task_state_t state; 50 unsigned int references; 51 isc_eventlist_t events; 52 isc_eventlist_t on_shutdown; 53 unsigned int nevents; 54 unsigned int quantum; 55 unsigned int flags; 56 time_t now; 57 char name[16]; 58 void * tag; 59 /* Locked by task manager lock. */ 60 LINK(isc_task_t) link; 61 LINK(isc_task_t) ready_link; 62 LINK(isc_task_t) ready_priority_link; 63 }; 64 65 #define TASK_F_SHUTTINGDOWN 0x01 66 #define TASK_F_PRIVILEGED 0x02 67 68 #define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ 69 != 0) 70 71 typedef ISC_LIST(isc_task_t) isc_tasklist_t; 72 73 struct isc_taskmgr { 74 /* Not locked. */ 75 /* Locked by task manager lock. */ 76 unsigned int default_quantum; 77 LIST(isc_task_t) tasks; 78 isc_tasklist_t ready_tasks; 79 isc_tasklist_t ready_priority_tasks; 80 isc_taskmgrmode_t mode; 81 unsigned int tasks_running; 82 unsigned int tasks_ready; 83 isc_boolean_t pause_requested; 84 isc_boolean_t exclusive_requested; 85 isc_boolean_t exiting; 86 87 /* 88 * Multiple threads can read/write 'excl' at the same time, so we need 89 * to protect the access. We can't use 'lock' since isc_task_detach() 90 * will try to acquire it. 91 */ 92 isc_task_t *excl; 93 unsigned int refs; 94 }; 95 96 #define DEFAULT_TASKMGR_QUANTUM 10 97 #define DEFAULT_DEFAULT_QUANTUM 5 98 #define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) 99 100 static isc_taskmgr_t *taskmgr = NULL; 101 102 static inline isc_boolean_t 103 empty_readyq(isc_taskmgr_t *manager); 104 105 static inline isc_task_t * 106 pop_readyq(isc_taskmgr_t *manager); 107 108 static inline void 109 push_readyq(isc_taskmgr_t *manager, isc_task_t *task); 110 111 /*** 112 *** Tasks. 113 ***/ 114 115 static void 116 task_finished(isc_task_t *task) { 117 isc_taskmgr_t *manager = task->manager; 118 119 REQUIRE(EMPTY(task->events)); 120 REQUIRE(task->nevents == 0); 121 REQUIRE(EMPTY(task->on_shutdown)); 122 REQUIRE(task->references == 0); 123 REQUIRE(task->state == task_state_done); 124 125 UNLINK(manager->tasks, task, link); 126 127 free(task); 128 } 129 130 isc_result_t 131 isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, 132 isc_task_t **taskp) 133 { 134 isc_task_t *task; 135 isc_boolean_t exiting; 136 137 REQUIRE(taskp != NULL && *taskp == NULL); 138 139 task = malloc(sizeof(*task)); 140 if (task == NULL) 141 return (ISC_R_NOMEMORY); 142 task->manager = manager; 143 task->state = task_state_idle; 144 task->references = 1; 145 INIT_LIST(task->events); 146 INIT_LIST(task->on_shutdown); 147 task->nevents = 0; 148 task->quantum = quantum; 149 task->flags = 0; 150 task->now = 0; 151 memset(task->name, 0, sizeof(task->name)); 152 task->tag = NULL; 153 INIT_LINK(task, link); 154 INIT_LINK(task, ready_link); 155 INIT_LINK(task, ready_priority_link); 156 157 exiting = ISC_FALSE; 158 if (!manager->exiting) { 159 if (task->quantum == 0) 160 task->quantum = manager->default_quantum; 161 APPEND(manager->tasks, task, link); 162 } else 163 exiting = ISC_TRUE; 164 165 if (exiting) { 166 free(task); 167 return (ISC_R_SHUTTINGDOWN); 168 } 169 170 *taskp = (isc_task_t *)task; 171 return (ISC_R_SUCCESS); 172 } 173 174 void 175 isc_task_attach(isc_task_t *source0, isc_task_t **targetp) { 176 isc_task_t *source = (isc_task_t *)source0; 177 178 /* 179 * Attach *targetp to source. 180 */ 181 182 REQUIRE(targetp != NULL && *targetp == NULL); 183 184 source->references++; 185 186 *targetp = (isc_task_t *)source; 187 } 188 189 static inline isc_boolean_t 190 task_shutdown(isc_task_t *task) { 191 isc_boolean_t was_idle = ISC_FALSE; 192 isc_event_t *event, *prev; 193 194 /* 195 * Caller must be holding the task's lock. 196 */ 197 198 if (! TASK_SHUTTINGDOWN(task)) { 199 task->flags |= TASK_F_SHUTTINGDOWN; 200 if (task->state == task_state_idle) { 201 INSIST(EMPTY(task->events)); 202 task->state = task_state_ready; 203 was_idle = ISC_TRUE; 204 } 205 INSIST(task->state == task_state_ready || 206 task->state == task_state_running); 207 208 /* 209 * Note that we post shutdown events LIFO. 210 */ 211 for (event = TAIL(task->on_shutdown); 212 event != NULL; 213 event = prev) { 214 prev = PREV(event, ev_link); 215 DEQUEUE(task->on_shutdown, event, ev_link); 216 ENQUEUE(task->events, event, ev_link); 217 task->nevents++; 218 } 219 } 220 221 return (was_idle); 222 } 223 224 /* 225 * Moves a task onto the appropriate run queue. 226 * 227 * Caller must NOT hold manager lock. 228 */ 229 static inline void 230 task_ready(isc_task_t *task) { 231 isc_taskmgr_t *manager = task->manager; 232 233 REQUIRE(task->state == task_state_ready); 234 235 push_readyq(manager, task); 236 } 237 238 static inline isc_boolean_t 239 task_detach(isc_task_t *task) { 240 241 /* 242 * Caller must be holding the task lock. 243 */ 244 245 REQUIRE(task->references > 0); 246 247 task->references--; 248 if (task->references == 0 && task->state == task_state_idle) { 249 INSIST(EMPTY(task->events)); 250 /* 251 * There are no references to this task, and no 252 * pending events. We could try to optimize and 253 * either initiate shutdown or clean up the task, 254 * depending on its state, but it's easier to just 255 * make the task ready and allow run() or the event 256 * loop to deal with shutting down and termination. 257 */ 258 task->state = task_state_ready; 259 return (ISC_TRUE); 260 } 261 262 return (ISC_FALSE); 263 } 264 265 void 266 isc_task_detach(isc_task_t **taskp) { 267 isc_task_t *task; 268 isc_boolean_t was_idle; 269 270 /* 271 * Detach *taskp from its task. 272 */ 273 274 REQUIRE(taskp != NULL); 275 task = (isc_task_t *)*taskp; 276 277 was_idle = task_detach(task); 278 279 if (was_idle) 280 task_ready(task); 281 282 *taskp = NULL; 283 } 284 285 static inline isc_boolean_t 286 task_send(isc_task_t *task, isc_event_t **eventp) { 287 isc_boolean_t was_idle = ISC_FALSE; 288 isc_event_t *event; 289 290 /* 291 * Caller must be holding the task lock. 292 */ 293 294 REQUIRE(eventp != NULL); 295 event = *eventp; 296 REQUIRE(event != NULL); 297 REQUIRE(event->ev_type > 0); 298 REQUIRE(task->state != task_state_done); 299 REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink)); 300 301 if (task->state == task_state_idle) { 302 was_idle = ISC_TRUE; 303 INSIST(EMPTY(task->events)); 304 task->state = task_state_ready; 305 } 306 INSIST(task->state == task_state_ready || 307 task->state == task_state_running); 308 ENQUEUE(task->events, event, ev_link); 309 task->nevents++; 310 *eventp = NULL; 311 312 return (was_idle); 313 } 314 315 void 316 isc_task_send(isc_task_t *task, isc_event_t **eventp) { 317 isc_boolean_t was_idle; 318 319 /* 320 * Send '*event' to 'task'. 321 */ 322 323 /* 324 * We're trying hard to hold locks for as short a time as possible. 325 * We're also trying to hold as few locks as possible. This is why 326 * some processing is deferred until after the lock is released. 327 */ 328 was_idle = task_send(task, eventp); 329 330 if (was_idle) { 331 /* 332 * We need to add this task to the ready queue. 333 * 334 * We've waited until now to do it because making a task 335 * ready requires locking the manager. If we tried to do 336 * this while holding the task lock, we could deadlock. 337 * 338 * We've changed the state to ready, so no one else will 339 * be trying to add this task to the ready queue. The 340 * only way to leave the ready state is by executing the 341 * task. It thus doesn't matter if events are added, 342 * removed, or a shutdown is started in the interval 343 * between the time we released the task lock, and the time 344 * we add the task to the ready queue. 345 */ 346 task_ready(task); 347 } 348 } 349 350 void 351 isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { 352 isc_boolean_t idle1, idle2; 353 isc_task_t *task; 354 355 /* 356 * Send '*event' to '*taskp' and then detach '*taskp' from its 357 * task. 358 */ 359 360 REQUIRE(taskp != NULL); 361 task = (isc_task_t *)*taskp; 362 363 idle1 = task_send(task, eventp); 364 idle2 = task_detach(task); 365 366 /* 367 * If idle1, then idle2 shouldn't be true as well since we're holding 368 * the task lock, and thus the task cannot switch from ready back to 369 * idle. 370 */ 371 INSIST(!(idle1 && idle2)); 372 373 if (idle1 || idle2) 374 task_ready(task); 375 376 *taskp = NULL; 377 } 378 379 #define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0) 380 381 static unsigned int 382 dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first, 383 isc_eventtype_t last, void *tag, 384 isc_eventlist_t *events, isc_boolean_t purging) 385 { 386 isc_event_t *event, *next_event; 387 unsigned int count = 0; 388 389 REQUIRE(last >= first); 390 391 /* 392 * Events matching 'sender', whose type is >= first and <= last, and 393 * whose tag is 'tag' will be dequeued. If 'purging', matching events 394 * which are marked as unpurgable will not be dequeued. 395 * 396 * sender == NULL means "any sender", and tag == NULL means "any tag". 397 */ 398 399 for (event = HEAD(task->events); event != NULL; event = next_event) { 400 next_event = NEXT(event, ev_link); 401 if (event->ev_type >= first && event->ev_type <= last && 402 (sender == NULL || event->ev_sender == sender) && 403 (tag == NULL || event->ev_tag == tag) && 404 (!purging || PURGE_OK(event))) { 405 DEQUEUE(task->events, event, ev_link); 406 task->nevents--; 407 ENQUEUE(*events, event, ev_link); 408 count++; 409 } 410 } 411 412 return (count); 413 } 414 415 unsigned int 416 isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, 417 isc_eventtype_t last, void *tag) 418 { 419 unsigned int count; 420 isc_eventlist_t events; 421 isc_event_t *event, *next_event; 422 423 /* 424 * Purge events from a task's event queue. 425 */ 426 427 ISC_LIST_INIT(events); 428 429 count = dequeue_events(task, sender, first, last, tag, &events, 430 ISC_TRUE); 431 432 for (event = HEAD(events); event != NULL; event = next_event) { 433 next_event = NEXT(event, ev_link); 434 ISC_LIST_UNLINK(events, event, ev_link); 435 isc_event_free(&event); 436 } 437 438 /* 439 * Note that purging never changes the state of the task. 440 */ 441 442 return (count); 443 } 444 445 void 446 isc_task_setname(isc_task_t *task, const char *name, void *tag) { 447 /* 448 * Name 'task'. 449 */ 450 451 strlcpy(task->name, name, sizeof(task->name)); 452 task->tag = tag; 453 } 454 455 /*** 456 *** Task Manager. 457 ***/ 458 459 /* 460 * Return ISC_TRUE if the current ready list for the manager, which is 461 * either ready_tasks or the ready_priority_tasks, depending on whether 462 * the manager is currently in normal or privileged execution mode. 463 * 464 * Caller must hold the task manager lock. 465 */ 466 static inline isc_boolean_t 467 empty_readyq(isc_taskmgr_t *manager) { 468 isc_tasklist_t queue; 469 470 if (manager->mode == isc_taskmgrmode_normal) 471 queue = manager->ready_tasks; 472 else 473 queue = manager->ready_priority_tasks; 474 475 return (ISC_TF(EMPTY(queue))); 476 } 477 478 /* 479 * Dequeue and return a pointer to the first task on the current ready 480 * list for the manager. 481 * If the task is privileged, dequeue it from the other ready list 482 * as well. 483 * 484 * Caller must hold the task manager lock. 485 */ 486 static inline isc_task_t * 487 pop_readyq(isc_taskmgr_t *manager) { 488 isc_task_t *task; 489 490 if (manager->mode == isc_taskmgrmode_normal) 491 task = HEAD(manager->ready_tasks); 492 else 493 task = HEAD(manager->ready_priority_tasks); 494 495 if (task != NULL) { 496 DEQUEUE(manager->ready_tasks, task, ready_link); 497 if (ISC_LINK_LINKED(task, ready_priority_link)) 498 DEQUEUE(manager->ready_priority_tasks, task, 499 ready_priority_link); 500 } 501 502 return (task); 503 } 504 505 /* 506 * Push 'task' onto the ready_tasks queue. If 'task' has the privilege 507 * flag set, then also push it onto the ready_priority_tasks queue. 508 * 509 * Caller must hold the task manager lock. 510 */ 511 static inline void 512 push_readyq(isc_taskmgr_t *manager, isc_task_t *task) { 513 ENQUEUE(manager->ready_tasks, task, ready_link); 514 if ((task->flags & TASK_F_PRIVILEGED) != 0) 515 ENQUEUE(manager->ready_priority_tasks, task, 516 ready_priority_link); 517 manager->tasks_ready++; 518 } 519 520 static void 521 dispatch(isc_taskmgr_t *manager) { 522 isc_task_t *task; 523 unsigned int total_dispatch_count = 0; 524 isc_tasklist_t new_ready_tasks; 525 isc_tasklist_t new_priority_tasks; 526 unsigned int tasks_ready = 0; 527 528 ISC_LIST_INIT(new_ready_tasks); 529 ISC_LIST_INIT(new_priority_tasks); 530 531 while (!FINISHED(manager)) { 532 if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || 533 empty_readyq(manager)) 534 break; 535 536 task = pop_readyq(manager); 537 if (task != NULL) { 538 unsigned int dispatch_count = 0; 539 isc_boolean_t done = ISC_FALSE; 540 isc_boolean_t requeue = ISC_FALSE; 541 isc_boolean_t finished = ISC_FALSE; 542 isc_event_t *event; 543 544 /* 545 * Note we only unlock the manager lock if we actually 546 * have a task to do. We must reacquire the manager 547 * lock before exiting the 'if (task != NULL)' block. 548 */ 549 manager->tasks_ready--; 550 manager->tasks_running++; 551 552 INSIST(task->state == task_state_ready); 553 task->state = task_state_running; 554 time(&task->now); 555 do { 556 if (!EMPTY(task->events)) { 557 event = HEAD(task->events); 558 DEQUEUE(task->events, event, ev_link); 559 task->nevents--; 560 561 /* 562 * Execute the event action. 563 */ 564 if (event->ev_action != NULL) { 565 (event->ev_action)( 566 (isc_task_t *)task, 567 event); 568 } 569 dispatch_count++; 570 total_dispatch_count++; 571 } 572 573 if (task->references == 0 && 574 EMPTY(task->events) && 575 !TASK_SHUTTINGDOWN(task)) { 576 isc_boolean_t was_idle; 577 578 /* 579 * There are no references and no 580 * pending events for this task, 581 * which means it will not become 582 * runnable again via an external 583 * action (such as sending an event 584 * or detaching). 585 * 586 * We initiate shutdown to prevent 587 * it from becoming a zombie. 588 * 589 * We do this here instead of in 590 * the "if EMPTY(task->events)" block 591 * below because: 592 * 593 * If we post no shutdown events, 594 * we want the task to finish. 595 * 596 * If we did post shutdown events, 597 * will still want the task's 598 * quantum to be applied. 599 */ 600 was_idle = task_shutdown(task); 601 INSIST(!was_idle); 602 } 603 604 if (EMPTY(task->events)) { 605 /* 606 * Nothing else to do for this task 607 * right now. 608 */ 609 if (task->references == 0 && 610 TASK_SHUTTINGDOWN(task)) { 611 /* 612 * The task is done. 613 */ 614 finished = ISC_TRUE; 615 task->state = task_state_done; 616 } else 617 task->state = task_state_idle; 618 done = ISC_TRUE; 619 } else if (dispatch_count >= task->quantum) { 620 /* 621 * Our quantum has expired, but 622 * there is more work to be done. 623 * We'll requeue it to the ready 624 * queue later. 625 * 626 * We don't check quantum until 627 * dispatching at least one event, 628 * so the minimum quantum is one. 629 */ 630 task->state = task_state_ready; 631 requeue = ISC_TRUE; 632 done = ISC_TRUE; 633 } 634 } while (!done); 635 636 if (finished) 637 task_finished(task); 638 639 manager->tasks_running--; 640 if (requeue) { 641 /* 642 * We know we're awake, so we don't have 643 * to wakeup any sleeping threads if the 644 * ready queue is empty before we requeue. 645 * 646 * A possible optimization if the queue is 647 * empty is to 'goto' the 'if (task != NULL)' 648 * block, avoiding the ENQUEUE of the task 649 * and the subsequent immediate DEQUEUE 650 * (since it is the only executable task). 651 * We don't do this because then we'd be 652 * skipping the exit_requested check. The 653 * cost of ENQUEUE is low anyway, especially 654 * when you consider that we'd have to do 655 * an extra EMPTY check to see if we could 656 * do the optimization. If the ready queue 657 * were usually nonempty, the 'optimization' 658 * might even hurt rather than help. 659 */ 660 ENQUEUE(new_ready_tasks, task, ready_link); 661 if ((task->flags & TASK_F_PRIVILEGED) != 0) 662 ENQUEUE(new_priority_tasks, task, 663 ready_priority_link); 664 tasks_ready++; 665 } 666 } 667 668 } 669 670 ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); 671 ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, 672 ready_priority_link); 673 manager->tasks_ready += tasks_ready; 674 if (empty_readyq(manager)) 675 manager->mode = isc_taskmgrmode_normal; 676 677 } 678 679 static void 680 manager_free(isc_taskmgr_t *manager) { 681 free(manager); 682 taskmgr = NULL; 683 } 684 685 isc_result_t 686 isc_taskmgr_create(unsigned int workers, 687 unsigned int default_quantum, isc_taskmgr_t **managerp) 688 { 689 unsigned int i, started = 0; 690 isc_taskmgr_t *manager; 691 692 /* 693 * Create a new task manager. 694 */ 695 696 REQUIRE(workers > 0); 697 REQUIRE(managerp != NULL && *managerp == NULL); 698 699 UNUSED(i); 700 UNUSED(started); 701 702 if (taskmgr != NULL) { 703 if (taskmgr->refs == 0) 704 return (ISC_R_SHUTTINGDOWN); 705 taskmgr->refs++; 706 *managerp = (isc_taskmgr_t *)taskmgr; 707 return (ISC_R_SUCCESS); 708 } 709 710 manager = malloc(sizeof(*manager)); 711 if (manager == NULL) 712 return (ISC_R_NOMEMORY); 713 manager->mode = isc_taskmgrmode_normal; 714 715 if (default_quantum == 0) 716 default_quantum = DEFAULT_DEFAULT_QUANTUM; 717 manager->default_quantum = default_quantum; 718 INIT_LIST(manager->tasks); 719 INIT_LIST(manager->ready_tasks); 720 INIT_LIST(manager->ready_priority_tasks); 721 manager->tasks_running = 0; 722 manager->tasks_ready = 0; 723 manager->exclusive_requested = ISC_FALSE; 724 manager->pause_requested = ISC_FALSE; 725 manager->exiting = ISC_FALSE; 726 manager->excl = NULL; 727 728 manager->refs = 1; 729 taskmgr = manager; 730 731 *managerp = (isc_taskmgr_t *)manager; 732 733 return (ISC_R_SUCCESS); 734 } 735 736 void 737 isc_taskmgr_destroy(isc_taskmgr_t **managerp) { 738 isc_taskmgr_t *manager; 739 isc_task_t *task; 740 unsigned int i; 741 742 /* 743 * Destroy '*managerp'. 744 */ 745 746 REQUIRE(managerp != NULL); 747 manager = (isc_taskmgr_t *)*managerp; 748 749 UNUSED(i); 750 751 manager->refs--; 752 if (manager->refs > 0) { 753 *managerp = NULL; 754 return; 755 } 756 757 /* 758 * Only one non-worker thread may ever call this routine. 759 * If a worker thread wants to initiate shutdown of the 760 * task manager, it should ask some non-worker thread to call 761 * isc_taskmgr_destroy(), e.g. by signalling a condition variable 762 * that the startup thread is sleeping on. 763 */ 764 765 /* 766 * Detach the exclusive task before acquiring the manager lock 767 */ 768 if (manager->excl != NULL) 769 isc_task_detach((isc_task_t **) &manager->excl); 770 771 /* 772 * Make sure we only get called once. 773 */ 774 INSIST(!manager->exiting); 775 manager->exiting = ISC_TRUE; 776 777 /* 778 * If privileged mode was on, turn it off. 779 */ 780 manager->mode = isc_taskmgrmode_normal; 781 782 /* 783 * Post shutdown event(s) to every task (if they haven't already been 784 * posted). 785 */ 786 for (task = HEAD(manager->tasks); 787 task != NULL; 788 task = NEXT(task, link)) { 789 if (task_shutdown(task)) 790 push_readyq(manager, task); 791 } 792 /* 793 * Dispatch the shutdown events. 794 */ 795 while (isc_taskmgr_ready((isc_taskmgr_t *)manager)) 796 (void)isc_taskmgr_dispatch((isc_taskmgr_t *)manager); 797 INSIST(ISC_LIST_EMPTY(manager->tasks)); 798 taskmgr = NULL; 799 800 manager_free(manager); 801 802 *managerp = NULL; 803 } 804 805 isc_boolean_t 806 isc_taskmgr_ready(isc_taskmgr_t *manager) { 807 isc_boolean_t is_ready; 808 809 if (manager == NULL) 810 manager = taskmgr; 811 if (manager == NULL) 812 return (ISC_FALSE); 813 814 is_ready = !empty_readyq(manager); 815 816 return (is_ready); 817 } 818 819 isc_result_t 820 isc_taskmgr_dispatch(isc_taskmgr_t *manager) { 821 if (manager == NULL) 822 manager = taskmgr; 823 if (manager == NULL) 824 return (ISC_R_NOTFOUND); 825 826 dispatch(manager); 827 828 return (ISC_R_SUCCESS); 829 } 830