isc_result_t isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp) { isc_task_t *task; isc_boolean_t exiting; REQUIRE(VALID_MANAGER(manager)); REQUIRE(taskp != NULL && *taskp == NULL); task = isc_mem_get(manager->mctx, sizeof(*task)); if (task == NULL) return (ISC_R_NOMEMORY); XTRACE("isc_task_create"); task->manager = manager; if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) { isc_mem_put(manager->mctx, task, sizeof(*task)); UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); return (ISC_R_UNEXPECTED); } task->state = task_state_idle; task->references = 1; INIT_LIST(task->events); INIT_LIST(task->on_shutdown); task->quantum = quantum; task->flags = 0; task->now = 0; #ifdef ISC_TASK_NAMES memset(task->name, 0, sizeof(task->name)); task->tag = NULL; #endif INIT_LINK(task, link); INIT_LINK(task, ready_link); exiting = ISC_FALSE; LOCK(&manager->lock); if (!manager->exiting) { if (task->quantum == 0) task->quantum = manager->default_quantum; APPEND(manager->tasks, task, link); } else exiting = ISC_TRUE; UNLOCK(&manager->lock); if (exiting) { DESTROYLOCK(&task->lock); isc_mem_put(manager->mctx, task, sizeof(*task)); return (ISC_R_SHUTTINGDOWN); } task->magic = TASK_MAGIC; *taskp = task; return (ISC_R_SUCCESS); }
ISC_TASKFUNC_SCOPE isc_result_t isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, isc_task_t **taskp) { isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; isc__task_t *task; isc_boolean_t exiting; isc_result_t result; REQUIRE(VALID_MANAGER(manager)); REQUIRE(taskp != NULL && *taskp == NULL); task = isc_mem_get(manager->mctx, sizeof(*task)); if (task == NULL) return (ISC_R_NOMEMORY); XTRACE("isc_task_create"); task->manager = manager; result = isc_mutex_init(&task->lock); if (result != ISC_R_SUCCESS) { isc_mem_put(manager->mctx, task, sizeof(*task)); return (result); } task->state = task_state_idle; task->references = 1; INIT_LIST(task->events); INIT_LIST(task->on_shutdown); task->quantum = quantum; task->flags = 0; task->now = 0; memset(task->name, 0, sizeof(task->name)); task->tag = NULL; INIT_LINK(task, link); INIT_LINK(task, ready_link); INIT_LINK(task, ready_priority_link); exiting = ISC_FALSE; LOCK(&manager->lock); if (!manager->exiting) { if (task->quantum == 0) task->quantum = manager->default_quantum; APPEND(manager->tasks, task, link); } else exiting = ISC_TRUE; UNLOCK(&manager->lock); if (exiting) { DESTROYLOCK(&task->lock); isc_mem_put(manager->mctx, task, sizeof(*task)); return (ISC_R_SHUTTINGDOWN); } task->common.methods = (isc_taskmethods_t *)&taskmethods; task->common.magic = ISCAPI_TASK_MAGIC; task->common.impmagic = TASK_MAGIC; *taskp = (isc_task_t *)task; return (ISC_R_SUCCESS); }
void isc_timermgr_poke(isc_timermgr_t *manager) { #ifdef ISC_PLATFORM_USETHREADS REQUIRE(VALID_MANAGER(manager)); SIGNAL(&manager->wakeup); #else UNUSED(manager); #endif }
ISC_TASKFUNC_SCOPE void isc__taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0) { isc__taskmgr_t *mgr = (isc__taskmgr_t *) mgr0; isc__task_t *task = (isc__task_t *) task0; REQUIRE(VALID_MANAGER(mgr)); REQUIRE(VALID_TASK(task)); if (mgr->excl != NULL) isc__task_detach((isc_task_t **) &mgr->excl); isc__task_attach(task0, (isc_task_t **) &mgr->excl); }
ISC_TIMERFUNC_SCOPE void isc__timermgr_poke(isc_timermgr_t *manager0) { #ifdef USE_TIMER_THREAD isc__timermgr_t *manager = (isc__timermgr_t *)manager0; REQUIRE(VALID_MANAGER(manager)); SIGNAL(&manager->wakeup); #else UNUSED(manager0); #endif }
ISC_TASKFUNC_SCOPE isc_result_t isc__taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp) { isc__taskmgr_t *mgr = (isc__taskmgr_t *) mgr0; REQUIRE(VALID_MANAGER(mgr)); REQUIRE(taskp != NULL && *taskp == NULL); if (mgr->excl == NULL) return (ISC_R_NOTFOUND); isc__task_attach((isc_task_t *) mgr->excl, taskp); return (ISC_R_SUCCESS); }
static inline void task_ready(isc_task_t *task) { isc_taskmgr_t *manager = task->manager; REQUIRE(VALID_MANAGER(manager)); REQUIRE(task->state == task_state_ready); XTRACE("task_ready"); LOCK(&manager->lock); ENQUEUE(manager->ready_tasks, task, ready_link); #ifdef ISC_PLATFORM_USETHREADS SIGNAL(&manager->work_available); #endif /* ISC_PLATFORM_USETHREADS */ UNLOCK(&manager->lock); }
/* * Moves a task onto the appropriate run queue. * * Caller must NOT hold manager lock. */ static inline void task_ready(isc__task_t *task) { isc__taskmgr_t *manager = task->manager; #ifdef USE_WORKER_THREADS isc_boolean_t has_privilege = isc__task_privilege((isc_task_t *) task); #endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); REQUIRE(task->state == task_state_ready); XTRACE("task_ready"); LOCK(&manager->lock); push_readyq(manager, task); #ifdef USE_WORKER_THREADS if (manager->mode == isc_taskmgrmode_normal || has_privilege) SIGNAL(&manager->work_available); #endif /* USE_WORKER_THREADS */ UNLOCK(&manager->lock); }
/*** *** Task Manager. ***/ static void dispatch(isc_taskmgr_t *manager) { isc_task_t *task; #ifndef ISC_PLATFORM_USETHREADS unsigned int total_dispatch_count = 0; isc_tasklist_t ready_tasks; #endif /* ISC_PLATFORM_USETHREADS */ REQUIRE(VALID_MANAGER(manager)); /* * Again we're trying to hold the lock for as short a time as possible * and to do as little locking and unlocking as possible. * * In both while loops, the appropriate lock must be held before the * while body starts. Code which acquired the lock at the top of * the loop would be more readable, but would result in a lot of * extra locking. Compare: * * Straightforward: * * LOCK(); * ... * UNLOCK(); * while (expression) { * LOCK(); * ... * UNLOCK(); * * Unlocked part here... * * LOCK(); * ... * UNLOCK(); * } * * Note how if the loop continues we unlock and then immediately lock. * For N iterations of the loop, this code does 2N+1 locks and 2N+1 * unlocks. Also note that the lock is not held when the while * condition is tested, which may or may not be important, depending * on the expression. * * As written: * * LOCK(); * while (expression) { * ... * UNLOCK(); * * Unlocked part here... * * LOCK(); * ... * } * UNLOCK(); * * For N iterations of the loop, this code does N+1 locks and N+1 * unlocks. The while expression is always protected by the lock. */ #ifndef ISC_PLATFORM_USETHREADS ISC_LIST_INIT(ready_tasks); #endif LOCK(&manager->lock); while (!FINISHED(manager)) { #ifdef ISC_PLATFORM_USETHREADS /* * For reasons similar to those given in the comment in * isc_task_send() above, it is safe for us to dequeue * the task while only holding the manager lock, and then * change the task to running state while only holding the * task lock. */ while ((EMPTY(manager->ready_tasks) || manager->exclusive_requested) && !FINISHED(manager)) { XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_WAIT, "wait")); WAIT(&manager->work_available, &manager->lock); XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_AWAKE, "awake")); } #else /* ISC_PLATFORM_USETHREADS */ if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || EMPTY(manager->ready_tasks)) break; #endif /* ISC_PLATFORM_USETHREADS */ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_WORKING, "working")); task = HEAD(manager->ready_tasks); if (task != NULL) { unsigned int dispatch_count = 0; isc_boolean_t done = ISC_FALSE; isc_boolean_t requeue = ISC_FALSE; isc_boolean_t finished = ISC_FALSE; isc_event_t *event; INSIST(VALID_TASK(task)); /* * Note we only unlock the manager lock if we actually * have a task to do. We must reacquire the manager * lock before exiting the 'if (task != NULL)' block. */ DEQUEUE(manager->ready_tasks, task, ready_link); manager->tasks_running++; UNLOCK(&manager->lock); LOCK(&task->lock); INSIST(task->state == task_state_ready); task->state = task_state_running; XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_RUNNING, "running")); isc_stdtime_get(&task->now); do { if (!EMPTY(task->events)) { event = HEAD(task->events); DEQUEUE(task->events, event, ev_link); /* * Execute the event action. */ XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_EXECUTE, "execute action")); if (event->ev_action != NULL) { UNLOCK(&task->lock); (event->ev_action)(task,event); LOCK(&task->lock); } dispatch_count++; #ifndef ISC_PLATFORM_USETHREADS total_dispatch_count++; #endif /* ISC_PLATFORM_USETHREADS */ } if (task->references == 0 && EMPTY(task->events) && !TASK_SHUTTINGDOWN(task)) { isc_boolean_t was_idle; /* * There are no references and no * pending events for this task, * which means it will not become * runnable again via an external * action (such as sending an event * or detaching). * * We initiate shutdown to prevent * it from becoming a zombie. * * We do this here instead of in * the "if EMPTY(task->events)" block * below because: * * If we post no shutdown events, * we want the task to finish. * * If we did post shutdown events, * will still want the task's * quantum to be applied. */ was_idle = task_shutdown(task); INSIST(!was_idle); } if (EMPTY(task->events)) { /* * Nothing else to do for this task * right now. */ XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_EMPTY, "empty")); if (task->references == 0 && TASK_SHUTTINGDOWN(task)) { /* * The task is done. */ XTRACE(isc_msgcat_get( isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_DONE, "done")); finished = ISC_TRUE; task->state = task_state_done; } else task->state = task_state_idle; done = ISC_TRUE; } else if (dispatch_count >= task->quantum) { /* * Our quantum has expired, but * there is more work to be done. * We'll requeue it to the ready * queue later. * * We don't check quantum until * dispatching at least one event, * so the minimum quantum is one. */ XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_QUANTUM, "quantum")); task->state = task_state_ready; requeue = ISC_TRUE; done = ISC_TRUE; } } while (!done); UNLOCK(&task->lock); if (finished) task_finished(task); LOCK(&manager->lock); manager->tasks_running--; #ifdef ISC_PLATFORM_USETHREADS if (manager->exclusive_requested && manager->tasks_running == 1) { SIGNAL(&manager->exclusive_granted); } #endif /* ISC_PLATFORM_USETHREADS */ if (requeue) { /* * We know we're awake, so we don't have * to wakeup any sleeping threads if the * ready queue is empty before we requeue. * * A possible optimization if the queue is * empty is to 'goto' the 'if (task != NULL)' * block, avoiding the ENQUEUE of the task * and the subsequent immediate DEQUEUE * (since it is the only executable task). * We don't do this because then we'd be * skipping the exit_requested check. The * cost of ENQUEUE is low anyway, especially * when you consider that we'd have to do * an extra EMPTY check to see if we could * do the optimization. If the ready queue * were usually nonempty, the 'optimization' * might even hurt rather than help. */ #ifdef ISC_PLATFORM_USETHREADS ENQUEUE(manager->ready_tasks, task, ready_link); #else ENQUEUE(ready_tasks, task, ready_link); #endif } } } #ifndef ISC_PLATFORM_USETHREADS ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link); #endif UNLOCK(&manager->lock); }
void isc_taskmgr_destroy(isc_taskmgr_t **managerp) { isc_taskmgr_t *manager; isc_task_t *task; unsigned int i; /* * Destroy '*managerp'. */ REQUIRE(managerp != NULL); manager = *managerp; REQUIRE(VALID_MANAGER(manager)); #ifndef ISC_PLATFORM_USETHREADS UNUSED(i); if (manager->refs > 1) { manager->refs--; *managerp = NULL; return; } #endif /* ISC_PLATFORM_USETHREADS */ XTHREADTRACE("isc_taskmgr_destroy"); /* * Only one non-worker thread may ever call this routine. * If a worker thread wants to initiate shutdown of the * task manager, it should ask some non-worker thread to call * isc_taskmgr_destroy(), e.g. by signalling a condition variable * that the startup thread is sleeping on. */ /* * Unlike elsewhere, we're going to hold this lock a long time. * We need to do so, because otherwise the list of tasks could * change while we were traversing it. * * This is also the only function where we will hold both the * task manager lock and a task lock at the same time. */ LOCK(&manager->lock); /* * Make sure we only get called once. */ INSIST(!manager->exiting); manager->exiting = ISC_TRUE; /* * Post shutdown event(s) to every task (if they haven't already been * posted). */ for (task = HEAD(manager->tasks); task != NULL; task = NEXT(task, link)) { LOCK(&task->lock); if (task_shutdown(task)) ENQUEUE(manager->ready_tasks, task, ready_link); UNLOCK(&task->lock); } #ifdef ISC_PLATFORM_USETHREADS /* * Wake up any sleeping workers. This ensures we get work done if * there's work left to do, and if there are already no tasks left * it will cause the workers to see manager->exiting. */ BROADCAST(&manager->work_available); UNLOCK(&manager->lock); /* * Wait for all the worker threads to exit. */ for (i = 0; i < manager->workers; i++) (void)isc_thread_join(manager->threads[i], NULL); #else /* ISC_PLATFORM_USETHREADS */ /* * Dispatch the shutdown events. */ UNLOCK(&manager->lock); while (isc__taskmgr_ready()) (void)isc__taskmgr_dispatch(); if (!ISC_LIST_EMPTY(manager->tasks)) isc_mem_printallactive(stderr); INSIST(ISC_LIST_EMPTY(manager->tasks)); #endif /* ISC_PLATFORM_USETHREADS */ manager_free(manager); *managerp = NULL; }
ISC_TIMERFUNC_SCOPE void isc__timermgr_destroy(isc_timermgr_t **managerp) { isc__timermgr_t *manager; isc_mem_t *mctx; /* * Destroy a timer manager. */ REQUIRE(managerp != NULL); manager = (isc__timermgr_t *)*managerp; REQUIRE(VALID_MANAGER(manager)); LOCK(&manager->lock); #ifdef USE_SHARED_MANAGER manager->refs--; if (manager->refs > 0) { UNLOCK(&manager->lock); *managerp = NULL; return; } timermgr = NULL; #endif /* USE_SHARED_MANAGER */ #ifndef USE_TIMER_THREAD isc__timermgr_dispatch((isc_timermgr_t *)manager); #endif REQUIRE(EMPTY(manager->timers)); manager->done = ISC_TRUE; #ifdef USE_TIMER_THREAD XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, ISC_MSG_SIGNALDESTROY, "signal (destroy)")); SIGNAL(&manager->wakeup); #endif /* USE_TIMER_THREAD */ UNLOCK(&manager->lock); #ifdef USE_TIMER_THREAD /* * Wait for thread to exit. */ if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS) UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_thread_join() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); #endif /* USE_TIMER_THREAD */ /* * Clean up. */ #ifdef USE_TIMER_THREAD (void)isc_condition_destroy(&manager->wakeup); #endif /* USE_TIMER_THREAD */ DESTROYLOCK(&manager->lock); isc_heap_destroy(&manager->heap); manager->common.impmagic = 0; manager->common.magic = 0; mctx = manager->mctx; isc_mem_put(mctx, manager, sizeof(*manager)); isc_mem_detach(&mctx); *managerp = NULL; #ifdef USE_SHARED_MANAGER timermgr = NULL; #endif }
ISC_TIMERFUNC_SCOPE isc_result_t isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type, isc_time_t *expires, isc_interval_t *interval, isc_boolean_t purge) { isc__timer_t *timer = (isc__timer_t *)timer0; isc_time_t now; isc__timermgr_t *manager; isc_result_t result; /* * Change the timer's type, expires, and interval values to the given * values. If 'purge' is ISC_TRUE, any pending events from this timer * are purged from its task's event queue. */ REQUIRE(VALID_TIMER(timer)); manager = timer->manager; REQUIRE(VALID_MANAGER(manager)); if (expires == NULL) expires = isc_time_epoch; if (interval == NULL) interval = isc_interval_zero; REQUIRE(type == isc_timertype_inactive || !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); REQUIRE(type != isc_timertype_limited || !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); /* * Get current time. */ if (type != isc_timertype_inactive) { TIME_NOW(&now); } else { /* * We don't have to do this, but it keeps the compiler from * complaining about "now" possibly being used without being * set, even though it will never actually happen. */ isc_time_settoepoch(&now); } LOCK(&manager->lock); LOCK(&timer->lock); if (purge) (void)isc_task_purgerange(timer->task, timer, ISC_TIMEREVENT_FIRSTEVENT, ISC_TIMEREVENT_LASTEVENT, NULL); timer->type = type; timer->expires = *expires; timer->interval = *interval; if (type == isc_timertype_once && !isc_interval_iszero(interval)) { result = isc_time_add(&now, interval, &timer->idle); } else { isc_time_settoepoch(&timer->idle); result = ISC_R_SUCCESS; } if (result == ISC_R_SUCCESS) { if (type == isc_timertype_inactive) { deschedule(timer); result = ISC_R_SUCCESS; } else result = schedule(timer, &now, ISC_TRUE); } UNLOCK(&timer->lock); UNLOCK(&manager->lock); return (result); }
ISC_TIMERFUNC_SCOPE isc_result_t isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type, isc_time_t *expires, isc_interval_t *interval, isc_task_t *task, isc_taskaction_t action, const void *arg, isc_timer_t **timerp) { isc__timermgr_t *manager = (isc__timermgr_t *)manager0; isc__timer_t *timer; isc_result_t result; isc_time_t now; /* * Create a new 'type' timer managed by 'manager'. The timers * parameters are specified by 'expires' and 'interval'. Events * will be posted to 'task' and when dispatched 'action' will be * called with 'arg' as the arg value. The new timer is returned * in 'timerp'. */ REQUIRE(VALID_MANAGER(manager)); REQUIRE(task != NULL); REQUIRE(action != NULL); if (expires == NULL) expires = isc_time_epoch; if (interval == NULL) interval = isc_interval_zero; REQUIRE(type == isc_timertype_inactive || !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); REQUIRE(timerp != NULL && *timerp == NULL); REQUIRE(type != isc_timertype_limited || !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); /* * Get current time. */ if (type != isc_timertype_inactive) { TIME_NOW(&now); } else { /* * We don't have to do this, but it keeps the compiler from * complaining about "now" possibly being used without being * set, even though it will never actually happen. */ isc_time_settoepoch(&now); } timer = isc_mem_get(manager->mctx, sizeof(*timer)); if (timer == NULL) return (ISC_R_NOMEMORY); timer->manager = manager; timer->references = 1; if (type == isc_timertype_once && !isc_interval_iszero(interval)) { result = isc_time_add(&now, interval, &timer->idle); if (result != ISC_R_SUCCESS) { isc_mem_put(manager->mctx, timer, sizeof(*timer)); return (result); } } else isc_time_settoepoch(&timer->idle); timer->type = type; timer->expires = *expires; timer->interval = *interval; timer->task = NULL; isc_task_attach(task, &timer->task); timer->action = action; /* * Removing the const attribute from "arg" is the best of two * evils here. If the timer->arg member is made const, then * it affects a great many recipients of the timer event * which did not pass in an "arg" that was truly const. * Changing isc_timer_create() to not have "arg" prototyped as const, * though, can cause compilers warnings for calls that *do* * have a truly const arg. The caller will have to carefully * keep track of whether arg started as a true const. */ DE_CONST(arg, timer->arg); timer->index = 0; result = isc_mutex_init(&timer->lock); if (result != ISC_R_SUCCESS) { isc_task_detach(&timer->task); isc_mem_put(manager->mctx, timer, sizeof(*timer)); return (result); } ISC_LINK_INIT(timer, link); timer->common.impmagic = TIMER_MAGIC; timer->common.magic = ISCAPI_TIMER_MAGIC; timer->common.methods = (isc_timermethods_t *)&timermethods; LOCK(&manager->lock); /* * Note we don't have to lock the timer like we normally would because * there are no external references to it yet. */ if (type != isc_timertype_inactive) result = schedule(timer, &now, ISC_TRUE); else result = ISC_R_SUCCESS; if (result == ISC_R_SUCCESS) APPEND(manager->timers, timer, link); UNLOCK(&manager->lock); if (result != ISC_R_SUCCESS) { timer->common.impmagic = 0; timer->common.magic = 0; DESTROYLOCK(&timer->lock); isc_task_detach(&timer->task); isc_mem_put(manager->mctx, timer, sizeof(*timer)); return (result); } *timerp = (isc_timer_t *)timer; return (ISC_R_SUCCESS); }
void isc_timermgr_destroy(isc_timermgr_t **managerp) { isc_timermgr_t *manager; isc_mem_t *mctx; /* * Destroy a timer manager. */ REQUIRE(managerp != NULL); manager = *managerp; REQUIRE(VALID_MANAGER(manager)); LOCK(&manager->lock); #ifndef ISC_PLATFORM_USETHREADS if (manager->refs > 1) { manager->refs--; UNLOCK(&manager->lock); *managerp = NULL; return; } isc__timermgr_dispatch(); #endif /* ISC_PLATFORM_USETHREADS */ REQUIRE(EMPTY(manager->timers)); manager->done = ISC_TRUE; #ifdef ISC_PLATFORM_USETHREADS XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, ISC_MSG_SIGNALDESTROY, "signal (destroy)")); SIGNAL(&manager->wakeup); #endif /* ISC_PLATFORM_USETHREADS */ UNLOCK(&manager->lock); #ifdef ISC_PLATFORM_USETHREADS /* * Wait for thread to exit. */ if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS) UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_thread_join() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); #endif /* ISC_PLATFORM_USETHREADS */ /* * Clean up. */ #ifdef ISC_PLATFORM_USETHREADS (void)isc_condition_destroy(&manager->wakeup); #endif /* ISC_PLATFORM_USETHREADS */ DESTROYLOCK(&manager->lock); isc_heap_destroy(&manager->heap); manager->magic = 0; mctx = manager->mctx; isc_mem_put(mctx, manager, sizeof(*manager)); isc_mem_detach(&mctx); *managerp = NULL; }