Annotation of embedaddon/ntp/lib/isc/task.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2004-2008  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: task.c,v 1.107 2008/03/27 23:46:57 tbox Exp $ */
                     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: #ifndef ISC_PLATFORM_USETHREADS
                     44: #include "task_p.h"
                     45: #endif /* ISC_PLATFORM_USETHREADS */
                     46: 
                     47: #ifdef ISC_TASK_TRACE
                     48: #define XTRACE(m)              fprintf(stderr, "task %p thread %lu: %s\n", \
                     49:                                       task, isc_thread_self(), (m))
                     50: #define XTTRACE(t, m)          fprintf(stderr, "task %p thread %lu: %s\n", \
                     51:                                       (t), isc_thread_self(), (m))
                     52: #define XTHREADTRACE(m)                fprintf(stderr, "thread %lu: %s\n", \
                     53:                                       isc_thread_self(), (m))
                     54: #else
                     55: #define XTRACE(m)
                     56: #define XTTRACE(t, m)
                     57: #define XTHREADTRACE(m)
                     58: #endif
                     59: 
                     60: /***
                     61:  *** Types.
                     62:  ***/
                     63: 
                     64: typedef enum {
                     65:        task_state_idle, task_state_ready, task_state_running,
                     66:        task_state_done
                     67: } task_state_t;
                     68: 
                     69: #ifdef HAVE_LIBXML2
                     70: static const char *statenames[] = {
                     71:        "idle", "ready", "running", "done",
                     72: };
                     73: #endif
                     74: 
                     75: #define TASK_MAGIC                     ISC_MAGIC('T', 'A', 'S', 'K')
                     76: #define VALID_TASK(t)                  ISC_MAGIC_VALID(t, TASK_MAGIC)
                     77: 
                     78: struct isc_task {
                     79:        /* Not locked. */
                     80:        unsigned int                    magic;
                     81:        isc_taskmgr_t *                 manager;
                     82:        isc_mutex_t                     lock;
                     83:        /* Locked by task lock. */
                     84:        task_state_t                    state;
                     85:        unsigned int                    references;
                     86:        isc_eventlist_t                 events;
                     87:        isc_eventlist_t                 on_shutdown;
                     88:        unsigned int                    quantum;
                     89:        unsigned int                    flags;
                     90:        isc_stdtime_t                   now;
                     91:        char                            name[16];
                     92:        void *                          tag;
                     93:        /* Locked by task manager lock. */
                     94:        LINK(isc_task_t)                link;
                     95:        LINK(isc_task_t)                ready_link;
                     96: };
                     97: 
                     98: #define TASK_F_SHUTTINGDOWN            0x01
                     99: 
                    100: #define TASK_SHUTTINGDOWN(t)           (((t)->flags & TASK_F_SHUTTINGDOWN) \
                    101:                                         != 0)
                    102: 
                    103: #define TASK_MANAGER_MAGIC             ISC_MAGIC('T', 'S', 'K', 'M')
                    104: #define VALID_MANAGER(m)               ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)
                    105: 
                    106: struct isc_taskmgr {
                    107:        /* Not locked. */
                    108:        unsigned int                    magic;
                    109:        isc_mem_t *                     mctx;
                    110:        isc_mutex_t                     lock;
                    111: #ifdef ISC_PLATFORM_USETHREADS
                    112:        unsigned int                    workers;
                    113:        isc_thread_t *                  threads;
                    114: #endif /* ISC_PLATFORM_USETHREADS */
                    115:        /* Locked by task manager lock. */
                    116:        unsigned int                    default_quantum;
                    117:        LIST(isc_task_t)                tasks;
                    118:        isc_tasklist_t                  ready_tasks;
                    119: #ifdef ISC_PLATFORM_USETHREADS
                    120:        isc_condition_t                 work_available;
                    121:        isc_condition_t                 exclusive_granted;
                    122: #endif /* ISC_PLATFORM_USETHREADS */
                    123:        unsigned int                    tasks_running;
                    124:        isc_boolean_t                   exclusive_requested;
                    125:        isc_boolean_t                   exiting;
                    126: #ifndef ISC_PLATFORM_USETHREADS
                    127:        unsigned int                    refs;
                    128: #endif /* ISC_PLATFORM_USETHREADS */
                    129: };
                    130: 
                    131: #define DEFAULT_TASKMGR_QUANTUM                10
                    132: #define DEFAULT_DEFAULT_QUANTUM                5
                    133: #define FINISHED(m)                    ((m)->exiting && EMPTY((m)->tasks))
                    134: 
                    135: #ifndef ISC_PLATFORM_USETHREADS
                    136: static isc_taskmgr_t *taskmgr = NULL;
                    137: #endif /* ISC_PLATFORM_USETHREADS */
                    138: 
                    139: /***
                    140:  *** Tasks.
                    141:  ***/
                    142: 
                    143: static void
                    144: task_finished(isc_task_t *task) {
                    145:        isc_taskmgr_t *manager = task->manager;
                    146: 
                    147:        REQUIRE(EMPTY(task->events));
                    148:        REQUIRE(EMPTY(task->on_shutdown));
                    149:        REQUIRE(task->references == 0);
                    150:        REQUIRE(task->state == task_state_done);
                    151: 
                    152:        XTRACE("task_finished");
                    153: 
                    154:        LOCK(&manager->lock);
                    155:        UNLINK(manager->tasks, task, link);
                    156: #ifdef ISC_PLATFORM_USETHREADS
                    157:        if (FINISHED(manager)) {
                    158:                /*
                    159:                 * All tasks have completed and the
                    160:                 * task manager is exiting.  Wake up
                    161:                 * any idle worker threads so they
                    162:                 * can exit.
                    163:                 */
                    164:                BROADCAST(&manager->work_available);
                    165:        }
                    166: #endif /* ISC_PLATFORM_USETHREADS */
                    167:        UNLOCK(&manager->lock);
                    168: 
                    169:        DESTROYLOCK(&task->lock);
                    170:        task->magic = 0;
                    171:        isc_mem_put(manager->mctx, task, sizeof(*task));
                    172: }
                    173: 
                    174: isc_result_t
                    175: isc_task_create(isc_taskmgr_t *manager, unsigned int quantum,
                    176:                isc_task_t **taskp)
                    177: {
                    178:        isc_task_t *task;
                    179:        isc_boolean_t exiting;
                    180:        isc_result_t result;
                    181: 
                    182:        REQUIRE(VALID_MANAGER(manager));
                    183:        REQUIRE(taskp != NULL && *taskp == NULL);
                    184: 
                    185:        task = isc_mem_get(manager->mctx, sizeof(*task));
                    186:        if (task == NULL)
                    187:                return (ISC_R_NOMEMORY);
                    188:        XTRACE("isc_task_create");
                    189:        task->manager = manager;
                    190:        result = isc_mutex_init(&task->lock);
                    191:        if (result != ISC_R_SUCCESS) {
                    192:                isc_mem_put(manager->mctx, task, sizeof(*task));
                    193:                return (result);
                    194:        }
                    195:        task->state = task_state_idle;
                    196:        task->references = 1;
                    197:        INIT_LIST(task->events);
                    198:        INIT_LIST(task->on_shutdown);
                    199:        task->quantum = quantum;
                    200:        task->flags = 0;
                    201:        task->now = 0;
                    202:        memset(task->name, 0, sizeof(task->name));
                    203:        task->tag = NULL;
                    204:        INIT_LINK(task, link);
                    205:        INIT_LINK(task, ready_link);
                    206: 
                    207:        exiting = ISC_FALSE;
                    208:        LOCK(&manager->lock);
                    209:        if (!manager->exiting) {
                    210:                if (task->quantum == 0)
                    211:                        task->quantum = manager->default_quantum;
                    212:                APPEND(manager->tasks, task, link);
                    213:        } else
                    214:                exiting = ISC_TRUE;
                    215:        UNLOCK(&manager->lock);
                    216: 
                    217:        if (exiting) {
                    218:                DESTROYLOCK(&task->lock);
                    219:                isc_mem_put(manager->mctx, task, sizeof(*task));
                    220:                return (ISC_R_SHUTTINGDOWN);
                    221:        }
                    222: 
                    223:        task->magic = TASK_MAGIC;
                    224:        *taskp = task;
                    225: 
                    226:        return (ISC_R_SUCCESS);
                    227: }
                    228: 
                    229: void
                    230: isc_task_attach(isc_task_t *source, isc_task_t **targetp) {
                    231: 
                    232:        /*
                    233:         * Attach *targetp to source.
                    234:         */
                    235: 
                    236:        REQUIRE(VALID_TASK(source));
                    237:        REQUIRE(targetp != NULL && *targetp == NULL);
                    238: 
                    239:        XTTRACE(source, "isc_task_attach");
                    240: 
                    241:        LOCK(&source->lock);
                    242:        source->references++;
                    243:        UNLOCK(&source->lock);
                    244: 
                    245:        *targetp = source;
                    246: }
                    247: 
                    248: static inline isc_boolean_t
                    249: task_shutdown(isc_task_t *task) {
                    250:        isc_boolean_t was_idle = ISC_FALSE;
                    251:        isc_event_t *event, *prev;
                    252: 
                    253:        /*
                    254:         * Caller must be holding the task's lock.
                    255:         */
                    256: 
                    257:        XTRACE("task_shutdown");
                    258: 
                    259:        if (! TASK_SHUTTINGDOWN(task)) {
                    260:                XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    261:                                      ISC_MSG_SHUTTINGDOWN, "shutting down"));
                    262:                task->flags |= TASK_F_SHUTTINGDOWN;
                    263:                if (task->state == task_state_idle) {
                    264:                        INSIST(EMPTY(task->events));
                    265:                        task->state = task_state_ready;
                    266:                        was_idle = ISC_TRUE;
                    267:                }
                    268:                INSIST(task->state == task_state_ready ||
                    269:                       task->state == task_state_running);
                    270:                /*
                    271:                 * Note that we post shutdown events LIFO.
                    272:                 */
                    273:                for (event = TAIL(task->on_shutdown);
                    274:                     event != NULL;
                    275:                     event = prev) {
                    276:                        prev = PREV(event, ev_link);
                    277:                        DEQUEUE(task->on_shutdown, event, ev_link);
                    278:                        ENQUEUE(task->events, event, ev_link);
                    279:                }
                    280:        }
                    281: 
                    282:        return (was_idle);
                    283: }
                    284: 
                    285: static inline void
                    286: task_ready(isc_task_t *task) {
                    287:        isc_taskmgr_t *manager = task->manager;
                    288: 
                    289:        REQUIRE(VALID_MANAGER(manager));
                    290:        REQUIRE(task->state == task_state_ready);
                    291: 
                    292:        XTRACE("task_ready");
                    293: 
                    294:        LOCK(&manager->lock);
                    295: 
                    296:        ENQUEUE(manager->ready_tasks, task, ready_link);
                    297: #ifdef ISC_PLATFORM_USETHREADS
                    298:        SIGNAL(&manager->work_available);
                    299: #endif /* ISC_PLATFORM_USETHREADS */
                    300: 
                    301:        UNLOCK(&manager->lock);
                    302: }
                    303: 
                    304: static inline isc_boolean_t
                    305: task_detach(isc_task_t *task) {
                    306: 
                    307:        /*
                    308:         * Caller must be holding the task lock.
                    309:         */
                    310: 
                    311:        REQUIRE(task->references > 0);
                    312: 
                    313:        XTRACE("detach");
                    314: 
                    315:        task->references--;
                    316:        if (task->references == 0 && task->state == task_state_idle) {
                    317:                INSIST(EMPTY(task->events));
                    318:                /*
                    319:                 * There are no references to this task, and no
                    320:                 * pending events.  We could try to optimize and
                    321:                 * either initiate shutdown or clean up the task,
                    322:                 * depending on its state, but it's easier to just
                    323:                 * make the task ready and allow run() or the event
                    324:                 * loop to deal with shutting down and termination.
                    325:                 */
                    326:                task->state = task_state_ready;
                    327:                return (ISC_TRUE);
                    328:        }
                    329: 
                    330:        return (ISC_FALSE);
                    331: }
                    332: 
                    333: void
                    334: isc_task_detach(isc_task_t **taskp) {
                    335:        isc_task_t *task;
                    336:        isc_boolean_t was_idle;
                    337: 
                    338:        /*
                    339:         * Detach *taskp from its task.
                    340:         */
                    341: 
                    342:        REQUIRE(taskp != NULL);
                    343:        task = *taskp;
                    344:        REQUIRE(VALID_TASK(task));
                    345: 
                    346:        XTRACE("isc_task_detach");
                    347: 
                    348:        LOCK(&task->lock);
                    349:        was_idle = task_detach(task);
                    350:        UNLOCK(&task->lock);
                    351: 
                    352:        if (was_idle)
                    353:                task_ready(task);
                    354: 
                    355:        *taskp = NULL;
                    356: }
                    357: 
                    358: static inline isc_boolean_t
                    359: task_send(isc_task_t *task, isc_event_t **eventp) {
                    360:        isc_boolean_t was_idle = ISC_FALSE;
                    361:        isc_event_t *event;
                    362: 
                    363:        /*
                    364:         * Caller must be holding the task lock.
                    365:         */
                    366: 
                    367:        REQUIRE(eventp != NULL);
                    368:        event = *eventp;
                    369:        REQUIRE(event != NULL);
                    370:        REQUIRE(event->ev_type > 0);
                    371:        REQUIRE(task->state != task_state_done);
                    372: 
                    373:        XTRACE("task_send");
                    374: 
                    375:        if (task->state == task_state_idle) {
                    376:                was_idle = ISC_TRUE;
                    377:                INSIST(EMPTY(task->events));
                    378:                task->state = task_state_ready;
                    379:        }
                    380:        INSIST(task->state == task_state_ready ||
                    381:               task->state == task_state_running);
                    382:        ENQUEUE(task->events, event, ev_link);
                    383:        *eventp = NULL;
                    384: 
                    385:        return (was_idle);
                    386: }
                    387: 
                    388: void
                    389: isc_task_send(isc_task_t *task, isc_event_t **eventp) {
                    390:        isc_boolean_t was_idle;
                    391: 
                    392:        /*
                    393:         * Send '*event' to 'task'.
                    394:         */
                    395: 
                    396:        REQUIRE(VALID_TASK(task));
                    397: 
                    398:        XTRACE("isc_task_send");
                    399: 
                    400:        /*
                    401:         * We're trying hard to hold locks for as short a time as possible.
                    402:         * We're also trying to hold as few locks as possible.  This is why
                    403:         * some processing is deferred until after the lock is released.
                    404:         */
                    405:        LOCK(&task->lock);
                    406:        was_idle = task_send(task, eventp);
                    407:        UNLOCK(&task->lock);
                    408: 
                    409:        if (was_idle) {
                    410:                /*
                    411:                 * We need to add this task to the ready queue.
                    412:                 *
                    413:                 * We've waited until now to do it because making a task
                    414:                 * ready requires locking the manager.  If we tried to do
                    415:                 * this while holding the task lock, we could deadlock.
                    416:                 *
                    417:                 * We've changed the state to ready, so no one else will
                    418:                 * be trying to add this task to the ready queue.  The
                    419:                 * only way to leave the ready state is by executing the
                    420:                 * task.  It thus doesn't matter if events are added,
                    421:                 * removed, or a shutdown is started in the interval
                    422:                 * between the time we released the task lock, and the time
                    423:                 * we add the task to the ready queue.
                    424:                 */
                    425:                task_ready(task);
                    426:        }
                    427: }
                    428: 
                    429: void
                    430: isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
                    431:        isc_boolean_t idle1, idle2;
                    432:        isc_task_t *task;
                    433: 
                    434:        /*
                    435:         * Send '*event' to '*taskp' and then detach '*taskp' from its
                    436:         * task.
                    437:         */
                    438: 
                    439:        REQUIRE(taskp != NULL);
                    440:        task = *taskp;
                    441:        REQUIRE(VALID_TASK(task));
                    442: 
                    443:        XTRACE("isc_task_sendanddetach");
                    444: 
                    445:        LOCK(&task->lock);
                    446:        idle1 = task_send(task, eventp);
                    447:        idle2 = task_detach(task);
                    448:        UNLOCK(&task->lock);
                    449: 
                    450:        /*
                    451:         * If idle1, then idle2 shouldn't be true as well since we're holding
                    452:         * the task lock, and thus the task cannot switch from ready back to
                    453:         * idle.
                    454:         */
                    455:        INSIST(!(idle1 && idle2));
                    456: 
                    457:        if (idle1 || idle2)
                    458:                task_ready(task);
                    459: 
                    460:        *taskp = NULL;
                    461: }
                    462: 
                    463: #define PURGE_OK(event)        (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
                    464: 
                    465: static unsigned int
                    466: dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first,
                    467:               isc_eventtype_t last, void *tag,
                    468:               isc_eventlist_t *events, isc_boolean_t purging)
                    469: {
                    470:        isc_event_t *event, *next_event;
                    471:        unsigned int count = 0;
                    472: 
                    473:        REQUIRE(VALID_TASK(task));
                    474:        REQUIRE(last >= first);
                    475: 
                    476:        XTRACE("dequeue_events");
                    477: 
                    478:        /*
                    479:         * Events matching 'sender', whose type is >= first and <= last, and
                    480:         * whose tag is 'tag' will be dequeued.  If 'purging', matching events
                    481:         * which are marked as unpurgable will not be dequeued.
                    482:         *
                    483:         * sender == NULL means "any sender", and tag == NULL means "any tag".
                    484:         */
                    485: 
                    486:        LOCK(&task->lock);
                    487: 
                    488:        for (event = HEAD(task->events); event != NULL; event = next_event) {
                    489:                next_event = NEXT(event, ev_link);
                    490:                if (event->ev_type >= first && event->ev_type <= last &&
                    491:                    (sender == NULL || event->ev_sender == sender) &&
                    492:                    (tag == NULL || event->ev_tag == tag) &&
                    493:                    (!purging || PURGE_OK(event))) {
                    494:                        DEQUEUE(task->events, event, ev_link);
                    495:                        ENQUEUE(*events, event, ev_link);
                    496:                        count++;
                    497:                }
                    498:        }
                    499: 
                    500:        UNLOCK(&task->lock);
                    501: 
                    502:        return (count);
                    503: }
                    504: 
                    505: unsigned int
                    506: isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
                    507:                    isc_eventtype_t last, void *tag)
                    508: {
                    509:        unsigned int count;
                    510:        isc_eventlist_t events;
                    511:        isc_event_t *event, *next_event;
                    512: 
                    513:        /*
                    514:         * Purge events from a task's event queue.
                    515:         */
                    516: 
                    517:        XTRACE("isc_task_purgerange");
                    518: 
                    519:        ISC_LIST_INIT(events);
                    520: 
                    521:        count = dequeue_events(task, sender, first, last, tag, &events,
                    522:                               ISC_TRUE);
                    523: 
                    524:        for (event = HEAD(events); event != NULL; event = next_event) {
                    525:                next_event = NEXT(event, ev_link);
                    526:                isc_event_free(&event);
                    527:        }
                    528: 
                    529:        /*
                    530:         * Note that purging never changes the state of the task.
                    531:         */
                    532: 
                    533:        return (count);
                    534: }
                    535: 
                    536: unsigned int
                    537: isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type,
                    538:               void *tag)
                    539: {
                    540:        /*
                    541:         * Purge events from a task's event queue.
                    542:         */
                    543: 
                    544:        XTRACE("isc_task_purge");
                    545: 
                    546:        return (isc_task_purgerange(task, sender, type, type, tag));
                    547: }
                    548: 
                    549: isc_boolean_t
                    550: isc_task_purgeevent(isc_task_t *task, isc_event_t *event) {
                    551:        isc_event_t *curr_event, *next_event;
                    552: 
                    553:        /*
                    554:         * Purge 'event' from a task's event queue.
                    555:         *
                    556:         * XXXRTH:  WARNING:  This method may be removed before beta.
                    557:         */
                    558: 
                    559:        REQUIRE(VALID_TASK(task));
                    560: 
                    561:        /*
                    562:         * If 'event' is on the task's event queue, it will be purged,
                    563:         * unless it is marked as unpurgeable.  'event' does not have to be
                    564:         * on the task's event queue; in fact, it can even be an invalid
                    565:         * pointer.  Purging only occurs if the event is actually on the task's
                    566:         * event queue.
                    567:         *
                    568:         * Purging never changes the state of the task.
                    569:         */
                    570: 
                    571:        LOCK(&task->lock);
                    572:        for (curr_event = HEAD(task->events);
                    573:             curr_event != NULL;
                    574:             curr_event = next_event) {
                    575:                next_event = NEXT(curr_event, ev_link);
                    576:                if (curr_event == event && PURGE_OK(event)) {
                    577:                        DEQUEUE(task->events, curr_event, ev_link);
                    578:                        break;
                    579:                }
                    580:        }
                    581:        UNLOCK(&task->lock);
                    582: 
                    583:        if (curr_event == NULL)
                    584:                return (ISC_FALSE);
                    585: 
                    586:        isc_event_free(&curr_event);
                    587: 
                    588:        return (ISC_TRUE);
                    589: }
                    590: 
                    591: unsigned int
                    592: isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first,
                    593:                     isc_eventtype_t last, void *tag,
                    594:                     isc_eventlist_t *events)
                    595: {
                    596:        /*
                    597:         * Remove events from a task's event queue.
                    598:         */
                    599: 
                    600:        XTRACE("isc_task_unsendrange");
                    601: 
                    602:        return (dequeue_events(task, sender, first, last, tag, events,
                    603:                               ISC_FALSE));
                    604: }
                    605: 
                    606: unsigned int
                    607: isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type,
                    608:                void *tag, isc_eventlist_t *events)
                    609: {
                    610:        /*
                    611:         * Remove events from a task's event queue.
                    612:         */
                    613: 
                    614:        XTRACE("isc_task_unsend");
                    615: 
                    616:        return (dequeue_events(task, sender, type, type, tag, events,
                    617:                               ISC_FALSE));
                    618: }
                    619: 
                    620: isc_result_t
                    621: isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg)
                    622: {
                    623:        isc_boolean_t disallowed = ISC_FALSE;
                    624:        isc_result_t result = ISC_R_SUCCESS;
                    625:        isc_event_t *event;
                    626: 
                    627:        /*
                    628:         * Send a shutdown event with action 'action' and argument 'arg' when
                    629:         * 'task' is shutdown.
                    630:         */
                    631: 
                    632:        REQUIRE(VALID_TASK(task));
                    633:        REQUIRE(action != NULL);
                    634: 
                    635:        event = isc_event_allocate(task->manager->mctx,
                    636:                                   NULL,
                    637:                                   ISC_TASKEVENT_SHUTDOWN,
                    638:                                   action,
                    639:                                   arg,
                    640:                                   sizeof(*event));
                    641:        if (event == NULL)
                    642:                return (ISC_R_NOMEMORY);
                    643: 
                    644:        LOCK(&task->lock);
                    645:        if (TASK_SHUTTINGDOWN(task)) {
                    646:                disallowed = ISC_TRUE;
                    647:                result = ISC_R_SHUTTINGDOWN;
                    648:        } else
                    649:                ENQUEUE(task->on_shutdown, event, ev_link);
                    650:        UNLOCK(&task->lock);
                    651: 
                    652:        if (disallowed)
                    653:                isc_mem_put(task->manager->mctx, event, sizeof(*event));
                    654: 
                    655:        return (result);
                    656: }
                    657: 
                    658: void
                    659: isc_task_shutdown(isc_task_t *task) {
                    660:        isc_boolean_t was_idle;
                    661: 
                    662:        /*
                    663:         * Shutdown 'task'.
                    664:         */
                    665: 
                    666:        REQUIRE(VALID_TASK(task));
                    667: 
                    668:        LOCK(&task->lock);
                    669:        was_idle = task_shutdown(task);
                    670:        UNLOCK(&task->lock);
                    671: 
                    672:        if (was_idle)
                    673:                task_ready(task);
                    674: }
                    675: 
                    676: void
                    677: isc_task_destroy(isc_task_t **taskp) {
                    678: 
                    679:        /*
                    680:         * Destroy '*taskp'.
                    681:         */
                    682: 
                    683:        REQUIRE(taskp != NULL);
                    684: 
                    685:        isc_task_shutdown(*taskp);
                    686:        isc_task_detach(taskp);
                    687: }
                    688: 
                    689: void
                    690: isc_task_setname(isc_task_t *task, const char *name, void *tag) {
                    691: 
                    692:        /*
                    693:         * Name 'task'.
                    694:         */
                    695: 
                    696:        REQUIRE(VALID_TASK(task));
                    697: 
                    698:        LOCK(&task->lock);
                    699:        memset(task->name, 0, sizeof(task->name));
                    700:        strncpy(task->name, name, sizeof(task->name) - 1);
                    701:        task->tag = tag;
                    702:        UNLOCK(&task->lock);
                    703: }
                    704: 
                    705: const char *
                    706: isc_task_getname(isc_task_t *task) {
                    707:        return (task->name);
                    708: }
                    709: 
                    710: void *
                    711: isc_task_gettag(isc_task_t *task) {
                    712:        return (task->tag);
                    713: }
                    714: 
                    715: void
                    716: isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) {
                    717:        REQUIRE(VALID_TASK(task));
                    718:        REQUIRE(t != NULL);
                    719: 
                    720:        LOCK(&task->lock);
                    721: 
                    722:        *t = task->now;
                    723: 
                    724:        UNLOCK(&task->lock);
                    725: }
                    726: 
                    727: /***
                    728:  *** Task Manager.
                    729:  ***/
                    730: static void
                    731: dispatch(isc_taskmgr_t *manager) {
                    732:        isc_task_t *task;
                    733: #ifndef ISC_PLATFORM_USETHREADS
                    734:        unsigned int total_dispatch_count = 0;
                    735:        isc_tasklist_t ready_tasks;
                    736: #endif /* ISC_PLATFORM_USETHREADS */
                    737: 
                    738:        REQUIRE(VALID_MANAGER(manager));
                    739: 
                    740:        /*
                    741:         * Again we're trying to hold the lock for as short a time as possible
                    742:         * and to do as little locking and unlocking as possible.
                    743:         *
                    744:         * In both while loops, the appropriate lock must be held before the
                    745:         * while body starts.  Code which acquired the lock at the top of
                    746:         * the loop would be more readable, but would result in a lot of
                    747:         * extra locking.  Compare:
                    748:         *
                    749:         * Straightforward:
                    750:         *
                    751:         *      LOCK();
                    752:         *      ...
                    753:         *      UNLOCK();
                    754:         *      while (expression) {
                    755:         *              LOCK();
                    756:         *              ...
                    757:         *              UNLOCK();
                    758:         *
                    759:         *              Unlocked part here...
                    760:         *
                    761:         *              LOCK();
                    762:         *              ...
                    763:         *              UNLOCK();
                    764:         *      }
                    765:         *
                    766:         * Note how if the loop continues we unlock and then immediately lock.
                    767:         * For N iterations of the loop, this code does 2N+1 locks and 2N+1
                    768:         * unlocks.  Also note that the lock is not held when the while
                    769:         * condition is tested, which may or may not be important, depending
                    770:         * on the expression.
                    771:         *
                    772:         * As written:
                    773:         *
                    774:         *      LOCK();
                    775:         *      while (expression) {
                    776:         *              ...
                    777:         *              UNLOCK();
                    778:         *
                    779:         *              Unlocked part here...
                    780:         *
                    781:         *              LOCK();
                    782:         *              ...
                    783:         *      }
                    784:         *      UNLOCK();
                    785:         *
                    786:         * For N iterations of the loop, this code does N+1 locks and N+1
                    787:         * unlocks.  The while expression is always protected by the lock.
                    788:         */
                    789: 
                    790: #ifndef ISC_PLATFORM_USETHREADS
                    791:        ISC_LIST_INIT(ready_tasks);
                    792: #endif
                    793:        LOCK(&manager->lock);
                    794:        while (!FINISHED(manager)) {
                    795: #ifdef ISC_PLATFORM_USETHREADS
                    796:                /*
                    797:                 * For reasons similar to those given in the comment in
                    798:                 * isc_task_send() above, it is safe for us to dequeue
                    799:                 * the task while only holding the manager lock, and then
                    800:                 * change the task to running state while only holding the
                    801:                 * task lock.
                    802:                 */
                    803:                while ((EMPTY(manager->ready_tasks) ||
                    804:                        manager->exclusive_requested) &&
                    805:                        !FINISHED(manager))
                    806:                {
                    807:                        XTHREADTRACE(isc_msgcat_get(isc_msgcat,
                    808:                                                    ISC_MSGSET_GENERAL,
                    809:                                                    ISC_MSG_WAIT, "wait"));
                    810:                        WAIT(&manager->work_available, &manager->lock);
                    811:                        XTHREADTRACE(isc_msgcat_get(isc_msgcat,
                    812:                                                    ISC_MSGSET_TASK,
                    813:                                                    ISC_MSG_AWAKE, "awake"));
                    814:                }
                    815: #else /* ISC_PLATFORM_USETHREADS */
                    816:                if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM ||
                    817:                    EMPTY(manager->ready_tasks))
                    818:                        break;
                    819: #endif /* ISC_PLATFORM_USETHREADS */
                    820:                XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK,
                    821:                                            ISC_MSG_WORKING, "working"));
                    822: 
                    823:                task = HEAD(manager->ready_tasks);
                    824:                if (task != NULL) {
                    825:                        unsigned int dispatch_count = 0;
                    826:                        isc_boolean_t done = ISC_FALSE;
                    827:                        isc_boolean_t requeue = ISC_FALSE;
                    828:                        isc_boolean_t finished = ISC_FALSE;
                    829:                        isc_event_t *event;
                    830: 
                    831:                        INSIST(VALID_TASK(task));
                    832: 
                    833:                        /*
                    834:                         * Note we only unlock the manager lock if we actually
                    835:                         * have a task to do.  We must reacquire the manager
                    836:                         * lock before exiting the 'if (task != NULL)' block.
                    837:                         */
                    838:                        DEQUEUE(manager->ready_tasks, task, ready_link);
                    839:                        manager->tasks_running++;
                    840:                        UNLOCK(&manager->lock);
                    841: 
                    842:                        LOCK(&task->lock);
                    843:                        INSIST(task->state == task_state_ready);
                    844:                        task->state = task_state_running;
                    845:                        XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    846:                                              ISC_MSG_RUNNING, "running"));
                    847:                        isc_stdtime_get(&task->now);
                    848:                        do {
                    849:                                if (!EMPTY(task->events)) {
                    850:                                        event = HEAD(task->events);
                    851:                                        DEQUEUE(task->events, event, ev_link);
                    852: 
                    853:                                        /*
                    854:                                         * Execute the event action.
                    855:                                         */
                    856:                                        XTRACE(isc_msgcat_get(isc_msgcat,
                    857:                                                            ISC_MSGSET_TASK,
                    858:                                                            ISC_MSG_EXECUTE,
                    859:                                                            "execute action"));
                    860:                                        if (event->ev_action != NULL) {
                    861:                                                UNLOCK(&task->lock);
                    862:                                                (event->ev_action)(task,event);
                    863:                                                LOCK(&task->lock);
                    864:                                        }
                    865:                                        dispatch_count++;
                    866: #ifndef ISC_PLATFORM_USETHREADS
                    867:                                        total_dispatch_count++;
                    868: #endif /* ISC_PLATFORM_USETHREADS */
                    869:                                }
                    870: 
                    871:                                if (task->references == 0 &&
                    872:                                    EMPTY(task->events) &&
                    873:                                    !TASK_SHUTTINGDOWN(task)) {
                    874:                                        isc_boolean_t was_idle;
                    875: 
                    876:                                        /*
                    877:                                         * There are no references and no
                    878:                                         * pending events for this task,
                    879:                                         * which means it will not become
                    880:                                         * runnable again via an external
                    881:                                         * action (such as sending an event
                    882:                                         * or detaching).
                    883:                                         *
                    884:                                         * We initiate shutdown to prevent
                    885:                                         * it from becoming a zombie.
                    886:                                         *
                    887:                                         * We do this here instead of in
                    888:                                         * the "if EMPTY(task->events)" block
                    889:                                         * below because:
                    890:                                         *
                    891:                                         *      If we post no shutdown events,
                    892:                                         *      we want the task to finish.
                    893:                                         *
                    894:                                         *      If we did post shutdown events,
                    895:                                         *      will still want the task's
                    896:                                         *      quantum to be applied.
                    897:                                         */
                    898:                                        was_idle = task_shutdown(task);
                    899:                                        INSIST(!was_idle);
                    900:                                }
                    901: 
                    902:                                if (EMPTY(task->events)) {
                    903:                                        /*
                    904:                                         * Nothing else to do for this task
                    905:                                         * right now.
                    906:                                         */
                    907:                                        XTRACE(isc_msgcat_get(isc_msgcat,
                    908:                                                              ISC_MSGSET_TASK,
                    909:                                                              ISC_MSG_EMPTY,
                    910:                                                              "empty"));
                    911:                                        if (task->references == 0 &&
                    912:                                            TASK_SHUTTINGDOWN(task)) {
                    913:                                                /*
                    914:                                                 * The task is done.
                    915:                                                 */
                    916:                                                XTRACE(isc_msgcat_get(
                    917:                                                               isc_msgcat,
                    918:                                                               ISC_MSGSET_TASK,
                    919:                                                               ISC_MSG_DONE,
                    920:                                                               "done"));
                    921:                                                finished = ISC_TRUE;
                    922:                                                task->state = task_state_done;
                    923:                                        } else
                    924:                                                task->state = task_state_idle;
                    925:                                        done = ISC_TRUE;
                    926:                                } else if (dispatch_count >= task->quantum) {
                    927:                                        /*
                    928:                                         * Our quantum has expired, but
                    929:                                         * there is more work to be done.
                    930:                                         * We'll requeue it to the ready
                    931:                                         * queue later.
                    932:                                         *
                    933:                                         * We don't check quantum until
                    934:                                         * dispatching at least one event,
                    935:                                         * so the minimum quantum is one.
                    936:                                         */
                    937:                                        XTRACE(isc_msgcat_get(isc_msgcat,
                    938:                                                              ISC_MSGSET_TASK,
                    939:                                                              ISC_MSG_QUANTUM,
                    940:                                                              "quantum"));
                    941:                                        task->state = task_state_ready;
                    942:                                        requeue = ISC_TRUE;
                    943:                                        done = ISC_TRUE;
                    944:                                }
                    945:                        } while (!done);
                    946:                        UNLOCK(&task->lock);
                    947: 
                    948:                        if (finished)
                    949:                                task_finished(task);
                    950: 
                    951:                        LOCK(&manager->lock);
                    952:                        manager->tasks_running--;
                    953: #ifdef ISC_PLATFORM_USETHREADS
                    954:                        if (manager->exclusive_requested &&
                    955:                            manager->tasks_running == 1) {
                    956:                                SIGNAL(&manager->exclusive_granted);
                    957:                        }
                    958: #endif /* ISC_PLATFORM_USETHREADS */
                    959:                        if (requeue) {
                    960:                                /*
                    961:                                 * We know we're awake, so we don't have
                    962:                                 * to wakeup any sleeping threads if the
                    963:                                 * ready queue is empty before we requeue.
                    964:                                 *
                    965:                                 * A possible optimization if the queue is
                    966:                                 * empty is to 'goto' the 'if (task != NULL)'
                    967:                                 * block, avoiding the ENQUEUE of the task
                    968:                                 * and the subsequent immediate DEQUEUE
                    969:                                 * (since it is the only executable task).
                    970:                                 * We don't do this because then we'd be
                    971:                                 * skipping the exit_requested check.  The
                    972:                                 * cost of ENQUEUE is low anyway, especially
                    973:                                 * when you consider that we'd have to do
                    974:                                 * an extra EMPTY check to see if we could
                    975:                                 * do the optimization.  If the ready queue
                    976:                                 * were usually nonempty, the 'optimization'
                    977:                                 * might even hurt rather than help.
                    978:                                 */
                    979: #ifdef ISC_PLATFORM_USETHREADS
                    980:                                ENQUEUE(manager->ready_tasks, task,
                    981:                                        ready_link);
                    982: #else
                    983:                                ENQUEUE(ready_tasks, task, ready_link);
                    984: #endif
                    985:                        }
                    986:                }
                    987:        }
                    988: #ifndef ISC_PLATFORM_USETHREADS
                    989:        ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link);
                    990: #endif
                    991:        UNLOCK(&manager->lock);
                    992: }
                    993: 
                    994: #ifdef ISC_PLATFORM_USETHREADS
                    995: static isc_threadresult_t
                    996: #ifdef _WIN32
                    997: WINAPI
                    998: #endif
                    999: run(void *uap) {
                   1000:        isc_taskmgr_t *manager = uap;
                   1001: 
                   1002:        XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                   1003:                                    ISC_MSG_STARTING, "starting"));
                   1004: 
                   1005:        dispatch(manager);
                   1006: 
                   1007:        XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                   1008:                                    ISC_MSG_EXITING, "exiting"));
                   1009: 
                   1010:        return ((isc_threadresult_t)0);
                   1011: }
                   1012: #endif /* ISC_PLATFORM_USETHREADS */
                   1013: 
                   1014: static void
                   1015: manager_free(isc_taskmgr_t *manager) {
                   1016:        isc_mem_t *mctx;
                   1017: 
                   1018: #ifdef ISC_PLATFORM_USETHREADS
                   1019:        (void)isc_condition_destroy(&manager->exclusive_granted);
                   1020:        (void)isc_condition_destroy(&manager->work_available);
                   1021:        isc_mem_free(manager->mctx, manager->threads);
                   1022: #endif /* ISC_PLATFORM_USETHREADS */
                   1023:        DESTROYLOCK(&manager->lock);
                   1024:        manager->magic = 0;
                   1025:        mctx = manager->mctx;
                   1026:        isc_mem_put(mctx, manager, sizeof(*manager));
                   1027:        isc_mem_detach(&mctx);
                   1028: }
                   1029: 
                   1030: isc_result_t
                   1031: isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers,
                   1032:                   unsigned int default_quantum, isc_taskmgr_t **managerp)
                   1033: {
                   1034:        isc_result_t result;
                   1035:        unsigned int i, started = 0;
                   1036:        isc_taskmgr_t *manager;
                   1037: 
                   1038:        /*
                   1039:         * Create a new task manager.
                   1040:         */
                   1041: 
                   1042:        REQUIRE(workers > 0);
                   1043:        REQUIRE(managerp != NULL && *managerp == NULL);
                   1044: 
                   1045: #ifndef ISC_PLATFORM_USETHREADS
                   1046:        UNUSED(i);
                   1047:        UNUSED(started);
                   1048:        UNUSED(workers);
                   1049: 
                   1050:        if (taskmgr != NULL) {
                   1051:                taskmgr->refs++;
                   1052:                *managerp = taskmgr;
                   1053:                return (ISC_R_SUCCESS);
                   1054:        }
                   1055: #endif /* ISC_PLATFORM_USETHREADS */
                   1056: 
                   1057:        manager = isc_mem_get(mctx, sizeof(*manager));
                   1058:        if (manager == NULL)
                   1059:                return (ISC_R_NOMEMORY);
                   1060:        manager->magic = TASK_MANAGER_MAGIC;
                   1061:        manager->mctx = NULL;
                   1062:        result = isc_mutex_init(&manager->lock);
                   1063:        if (result != ISC_R_SUCCESS)
                   1064:                goto cleanup_mgr;
                   1065: 
                   1066: #ifdef ISC_PLATFORM_USETHREADS
                   1067:        manager->workers = 0;
                   1068:        manager->threads = isc_mem_allocate(mctx,
                   1069:                                            workers * sizeof(isc_thread_t));
                   1070:        if (manager->threads == NULL) {
                   1071:                result = ISC_R_NOMEMORY;
                   1072:                goto cleanup_lock;
                   1073:        }
                   1074:        if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) {
                   1075:                UNEXPECTED_ERROR(__FILE__, __LINE__,
                   1076:                                 "isc_condition_init() %s",
                   1077:                                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                   1078:                                                ISC_MSG_FAILED, "failed"));
                   1079:                result = ISC_R_UNEXPECTED;
                   1080:                goto cleanup_threads;
                   1081:        }
                   1082:        if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) {
                   1083:                UNEXPECTED_ERROR(__FILE__, __LINE__,
                   1084:                                 "isc_condition_init() %s",
                   1085:                                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                   1086:                                                ISC_MSG_FAILED, "failed"));
                   1087:                result = ISC_R_UNEXPECTED;
                   1088:                goto cleanup_workavailable;
                   1089:        }
                   1090: #endif /* ISC_PLATFORM_USETHREADS */
                   1091:        if (default_quantum == 0)
                   1092:                default_quantum = DEFAULT_DEFAULT_QUANTUM;
                   1093:        manager->default_quantum = default_quantum;
                   1094:        INIT_LIST(manager->tasks);
                   1095:        INIT_LIST(manager->ready_tasks);
                   1096:        manager->tasks_running = 0;
                   1097:        manager->exclusive_requested = ISC_FALSE;
                   1098:        manager->exiting = ISC_FALSE;
                   1099: 
                   1100:        isc_mem_attach(mctx, &manager->mctx);
                   1101: 
                   1102: #ifdef ISC_PLATFORM_USETHREADS
                   1103:        LOCK(&manager->lock);
                   1104:        /*
                   1105:         * Start workers.
                   1106:         */
                   1107:        for (i = 0; i < workers; i++) {
                   1108:                if (isc_thread_create(run, manager,
                   1109:                                      &manager->threads[manager->workers]) ==
                   1110:                    ISC_R_SUCCESS) {
                   1111:                        manager->workers++;
                   1112:                        started++;
                   1113:                }
                   1114:        }
                   1115:        UNLOCK(&manager->lock);
                   1116: 
                   1117:        if (started == 0) {
                   1118:                manager_free(manager);
                   1119:                return (ISC_R_NOTHREADS);
                   1120:        }
                   1121:        isc_thread_setconcurrency(workers);
                   1122: #else /* ISC_PLATFORM_USETHREADS */
                   1123:        manager->refs = 1;
                   1124:        taskmgr = manager;
                   1125: #endif /* ISC_PLATFORM_USETHREADS */
                   1126: 
                   1127:        *managerp = manager;
                   1128: 
                   1129:        return (ISC_R_SUCCESS);
                   1130: 
                   1131: #ifdef ISC_PLATFORM_USETHREADS
                   1132:  cleanup_workavailable:
                   1133:        (void)isc_condition_destroy(&manager->work_available);
                   1134:  cleanup_threads:
                   1135:        isc_mem_free(mctx, manager->threads);
                   1136:  cleanup_lock:
                   1137:        DESTROYLOCK(&manager->lock);
                   1138: #endif
                   1139:  cleanup_mgr:
                   1140:        isc_mem_put(mctx, manager, sizeof(*manager));
                   1141:        return (result);
                   1142: }
                   1143: 
                   1144: void
                   1145: isc_taskmgr_destroy(isc_taskmgr_t **managerp) {
                   1146:        isc_taskmgr_t *manager;
                   1147:        isc_task_t *task;
                   1148:        unsigned int i;
                   1149: 
                   1150:        /*
                   1151:         * Destroy '*managerp'.
                   1152:         */
                   1153: 
                   1154:        REQUIRE(managerp != NULL);
                   1155:        manager = *managerp;
                   1156:        REQUIRE(VALID_MANAGER(manager));
                   1157: 
                   1158: #ifndef ISC_PLATFORM_USETHREADS
                   1159:        UNUSED(i);
                   1160: 
                   1161:        if (manager->refs > 1) {
                   1162:                manager->refs--;
                   1163:                *managerp = NULL;
                   1164:                return;
                   1165:        }
                   1166: #endif /* ISC_PLATFORM_USETHREADS */
                   1167: 
                   1168:        XTHREADTRACE("isc_taskmgr_destroy");
                   1169:        /*
                   1170:         * Only one non-worker thread may ever call this routine.
                   1171:         * If a worker thread wants to initiate shutdown of the
                   1172:         * task manager, it should ask some non-worker thread to call
                   1173:         * isc_taskmgr_destroy(), e.g. by signalling a condition variable
                   1174:         * that the startup thread is sleeping on.
                   1175:         */
                   1176: 
                   1177:        /*
                   1178:         * Unlike elsewhere, we're going to hold this lock a long time.
                   1179:         * We need to do so, because otherwise the list of tasks could
                   1180:         * change while we were traversing it.
                   1181:         *
                   1182:         * This is also the only function where we will hold both the
                   1183:         * task manager lock and a task lock at the same time.
                   1184:         */
                   1185: 
                   1186:        LOCK(&manager->lock);
                   1187: 
                   1188:        /*
                   1189:         * Make sure we only get called once.
                   1190:         */
                   1191:        INSIST(!manager->exiting);
                   1192:        manager->exiting = ISC_TRUE;
                   1193: 
                   1194:        /*
                   1195:         * Post shutdown event(s) to every task (if they haven't already been
                   1196:         * posted).
                   1197:         */
                   1198:        for (task = HEAD(manager->tasks);
                   1199:             task != NULL;
                   1200:             task = NEXT(task, link)) {
                   1201:                LOCK(&task->lock);
                   1202:                if (task_shutdown(task))
                   1203:                        ENQUEUE(manager->ready_tasks, task, ready_link);
                   1204:                UNLOCK(&task->lock);
                   1205:        }
                   1206: #ifdef ISC_PLATFORM_USETHREADS
                   1207:        /*
                   1208:         * Wake up any sleeping workers.  This ensures we get work done if
                   1209:         * there's work left to do, and if there are already no tasks left
                   1210:         * it will cause the workers to see manager->exiting.
                   1211:         */
                   1212:        BROADCAST(&manager->work_available);
                   1213:        UNLOCK(&manager->lock);
                   1214: 
                   1215:        /*
                   1216:         * Wait for all the worker threads to exit.
                   1217:         */
                   1218:        for (i = 0; i < manager->workers; i++)
                   1219:                (void)isc_thread_join(manager->threads[i], NULL);
                   1220: #else /* ISC_PLATFORM_USETHREADS */
                   1221:        /*
                   1222:         * Dispatch the shutdown events.
                   1223:         */
                   1224:        UNLOCK(&manager->lock);
                   1225:        while (isc__taskmgr_ready())
                   1226:                (void)isc__taskmgr_dispatch();
                   1227:        if (!ISC_LIST_EMPTY(manager->tasks))
                   1228:                isc_mem_printallactive(stderr);
                   1229:        INSIST(ISC_LIST_EMPTY(manager->tasks));
                   1230: #endif /* ISC_PLATFORM_USETHREADS */
                   1231: 
                   1232:        manager_free(manager);
                   1233: 
                   1234:        *managerp = NULL;
                   1235: }
                   1236: 
                   1237: #ifndef ISC_PLATFORM_USETHREADS
                   1238: isc_boolean_t
                   1239: isc__taskmgr_ready(void) {
                   1240:        if (taskmgr == NULL)
                   1241:                return (ISC_FALSE);
                   1242:        return (ISC_TF(!ISC_LIST_EMPTY(taskmgr->ready_tasks)));
                   1243: }
                   1244: 
                   1245: isc_result_t
                   1246: isc__taskmgr_dispatch(void) {
                   1247:        isc_taskmgr_t *manager = taskmgr;
                   1248: 
                   1249:        if (taskmgr == NULL)
                   1250:                return (ISC_R_NOTFOUND);
                   1251: 
                   1252:        dispatch(manager);
                   1253: 
                   1254:        return (ISC_R_SUCCESS);
                   1255: }
                   1256: 
                   1257: #endif /* ISC_PLATFORM_USETHREADS */
                   1258: 
                   1259: isc_result_t
                   1260: isc_task_beginexclusive(isc_task_t *task) {
                   1261: #ifdef ISC_PLATFORM_USETHREADS
                   1262:        isc_taskmgr_t *manager = task->manager;
                   1263:        REQUIRE(task->state == task_state_running);
                   1264:        LOCK(&manager->lock);
                   1265:        if (manager->exclusive_requested) {
                   1266:                UNLOCK(&manager->lock);
                   1267:                return (ISC_R_LOCKBUSY);
                   1268:        }
                   1269:        manager->exclusive_requested = ISC_TRUE;
                   1270:        while (manager->tasks_running > 1) {
                   1271:                WAIT(&manager->exclusive_granted, &manager->lock);
                   1272:        }
                   1273:        UNLOCK(&manager->lock);
                   1274: #else
                   1275:        UNUSED(task);
                   1276: #endif
                   1277:        return (ISC_R_SUCCESS);
                   1278: }
                   1279: 
                   1280: void
                   1281: isc_task_endexclusive(isc_task_t *task) {
                   1282: #ifdef ISC_PLATFORM_USETHREADS
                   1283:        isc_taskmgr_t *manager = task->manager;
                   1284:        REQUIRE(task->state == task_state_running);
                   1285:        LOCK(&manager->lock);
                   1286:        REQUIRE(manager->exclusive_requested);
                   1287:        manager->exclusive_requested = ISC_FALSE;
                   1288:        BROADCAST(&manager->work_available);
                   1289:        UNLOCK(&manager->lock);
                   1290: #else
                   1291:        UNUSED(task);
                   1292: #endif
                   1293: }
                   1294: 
                   1295: #ifdef HAVE_LIBXML2
                   1296: 
                   1297: void
                   1298: isc_taskmgr_renderxml(isc_taskmgr_t *mgr, xmlTextWriterPtr writer)
                   1299: {
                   1300:        isc_task_t *task;
                   1301: 
                   1302:        LOCK(&mgr->lock);
                   1303: 
                   1304:        /*
                   1305:         * Write out the thread-model, and some details about each depending
                   1306:         * on which type is enabled.
                   1307:         */
                   1308:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model");
                   1309: #ifdef ISC_PLATFORM_USETHREADS
                   1310:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "type");
                   1311:        xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded");
                   1312:        xmlTextWriterEndElement(writer); /* type */
                   1313: 
                   1314:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "worker-threads");
                   1315:        xmlTextWriterWriteFormatString(writer, "%d", mgr->workers);
                   1316:        xmlTextWriterEndElement(writer); /* worker-threads */
                   1317: #else /* ISC_PLATFORM_USETHREADS */
                   1318:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "type");
                   1319:        xmlTextWriterWriteString(writer, ISC_XMLCHAR "non-threaded");
                   1320:        xmlTextWriterEndElement(writer); /* type */
                   1321: 
                   1322:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
                   1323:        xmlTextWriterWriteFormatString(writer, "%d", mgr->refs);
                   1324:        xmlTextWriterEndElement(writer); /* references */
                   1325: #endif /* ISC_PLATFORM_USETHREADS */
                   1326: 
                   1327:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum");
                   1328:        xmlTextWriterWriteFormatString(writer, "%d", mgr->default_quantum);
                   1329:        xmlTextWriterEndElement(writer); /* default-quantum */
                   1330: 
                   1331:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running");
                   1332:        xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_running);
                   1333:        xmlTextWriterEndElement(writer); /* tasks-running */
                   1334: 
                   1335:        xmlTextWriterEndElement(writer); /* thread-model */
                   1336: 
                   1337:        xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks");
                   1338:        task = ISC_LIST_HEAD(mgr->tasks);
                   1339:        while (task != NULL) {
                   1340:                LOCK(&task->lock);
                   1341:                xmlTextWriterStartElement(writer, ISC_XMLCHAR "task");
                   1342: 
                   1343:                if (task->name[0] != 0) {
                   1344:                        xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
                   1345:                        xmlTextWriterWriteFormatString(writer, "%s",
                   1346:                                                       task->name);
                   1347:                        xmlTextWriterEndElement(writer); /* name */
                   1348:                }
                   1349: 
                   1350:                xmlTextWriterStartElement(writer, ISC_XMLCHAR "references");
                   1351:                xmlTextWriterWriteFormatString(writer, "%d", task->references);
                   1352:                xmlTextWriterEndElement(writer); /* references */
                   1353: 
                   1354:                xmlTextWriterStartElement(writer, ISC_XMLCHAR "id");
                   1355:                xmlTextWriterWriteFormatString(writer, "%p", task);
                   1356:                xmlTextWriterEndElement(writer); /* id */
                   1357: 
                   1358:                xmlTextWriterStartElement(writer, ISC_XMLCHAR "state");
                   1359:                xmlTextWriterWriteFormatString(writer, "%s",
                   1360:                                               statenames[task->state]);
                   1361:                xmlTextWriterEndElement(writer); /* state */
                   1362: 
                   1363:                xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum");
                   1364:                xmlTextWriterWriteFormatString(writer, "%d", task->quantum);
                   1365:                xmlTextWriterEndElement(writer); /* quantum */
                   1366: 
                   1367:                xmlTextWriterEndElement(writer);
                   1368: 
                   1369:                UNLOCK(&task->lock);
                   1370:                task = ISC_LIST_NEXT(task, link);
                   1371:        }
                   1372:        xmlTextWriterEndElement(writer); /* tasks */
                   1373: 
                   1374:        UNLOCK(&mgr->lock);
                   1375: }
                   1376: #endif /* HAVE_LIBXML2 */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>