diff options
| author | 2019-12-16 16:16:22 +0000 | |
|---|---|---|
| committer | 2019-12-16 16:16:22 +0000 | |
| commit | 2fc5abb04c862f358e234358caea4444e15db068 (patch) | |
| tree | 1c77d5b577fef0399be14245b2108ca9c1a8819d /usr.sbin/bind/lib/isc/task.c | |
| parent | Need to include message size in the maximum buffer calculation. (diff) | |
| download | wireguard-openbsd-2fc5abb04c862f358e234358caea4444e15db068.tar.xz wireguard-openbsd-2fc5abb04c862f358e234358caea4444e15db068.zip | |
Update to bind-9.10.5-P3, which appears to have been the last ISC version.
We only use this tree to build dig and nslookup. Our previous version
predated edns0 support in those tools, and we want that. This is the worst
code I've looked at in years, with layers and layers of spaghetti abstraction
clearly unfit for reuse, but then reused anyways, and the old ones remain
behind. So this is a 8MB diff.
florian, sthen, and otto tried this merge before but failed.
Diffstat (limited to 'usr.sbin/bind/lib/isc/task.c')
| -rw-r--r-- | usr.sbin/bind/lib/isc/task.c | 1340 |
1 files changed, 1170 insertions, 170 deletions
diff --git a/usr.sbin/bind/lib/isc/task.c b/usr.sbin/bind/lib/isc/task.c index efffd536323..44da80c6771 100644 --- a/usr.sbin/bind/lib/isc/task.c +++ b/usr.sbin/bind/lib/isc/task.c @@ -1,8 +1,8 @@ /* - * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2004-2015, 2017 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * - * Permission to use, copy, modify, and distribute this software for any + * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * @@ -15,8 +15,6 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $ISC: task.c,v 1.91.18.6 2006/01/04 23:50:23 marka Exp $ */ - /*! \file * \author Principal Author: Bob Halley */ @@ -28,22 +26,45 @@ #include <config.h> +#include <isc/app.h> #include <isc/condition.h> #include <isc/event.h> +#include <isc/json.h> #include <isc/magic.h> #include <isc/mem.h> #include <isc/msgs.h> +#include <isc/once.h> #include <isc/platform.h> +#include <isc/print.h> #include <isc/string.h> #include <isc/task.h> #include <isc/thread.h> #include <isc/util.h> +#include <isc/xml.h> -#ifndef ISC_PLATFORM_USETHREADS -#include "task_p.h" -#endif /* ISC_PLATFORM_USETHREADS */ +#ifdef OPENSSL_LEAKS +#include <openssl/err.h> +#endif + +/*% + * For BIND9 internal applications: + * when built with threads we use multiple worker threads shared by the whole + * application. + * when built without threads we share a single global task manager and use + * an integrated event loop for socket, timer, and other generic task events. + * For generic library: + * we don't use either of them: an application can have multiple task managers + * whether or not it's threaded, and if the application is threaded each thread + * is expected to have a separate manager; no "worker threads" are shared by + * the application threads. + */ +#ifdef ISC_PLATFORM_USETHREADS +#define USE_WORKER_THREADS +#else +#define USE_SHARED_MANAGER +#endif /* ISC_PLATFORM_USETHREADS */ -#define ISC_TASK_NAMES 1 +#include "task_p.h" #ifdef ISC_TASK_TRACE #define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ @@ -67,32 +88,42 @@ typedef enum { task_state_done } task_state_t; +#if defined(HAVE_LIBXML2) || defined(HAVE_JSON) +static const char *statenames[] = { + "idle", "ready", "running", "done", +}; +#endif + #define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K') #define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC) -struct isc_task { +typedef struct isc__task isc__task_t; +typedef struct isc__taskmgr isc__taskmgr_t; + +struct isc__task { /* Not locked. */ - unsigned int magic; - isc_taskmgr_t * manager; + isc_task_t common; + isc__taskmgr_t * manager; isc_mutex_t lock; /* Locked by task lock. */ task_state_t state; unsigned int references; isc_eventlist_t events; isc_eventlist_t on_shutdown; + unsigned int nevents; unsigned int quantum; unsigned int flags; isc_stdtime_t now; -#ifdef ISC_TASK_NAMES char name[16]; void * tag; -#endif /* Locked by task manager lock. */ - LINK(isc_task_t) link; - LINK(isc_task_t) ready_link; + LINK(isc__task_t) link; + LINK(isc__task_t) ready_link; + LINK(isc__task_t) ready_priority_link; }; #define TASK_F_SHUTTINGDOWN 0x01 +#define TASK_F_PRIVILEGED 0x02 #define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ != 0) @@ -100,9 +131,11 @@ struct isc_task { #define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M') #define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC) -struct isc_taskmgr { +typedef ISC_LIST(isc__task_t) isc__tasklist_t; + +struct isc__taskmgr { /* Not locked. */ - unsigned int magic; + isc_taskmgr_t common; isc_mem_t * mctx; isc_mutex_t lock; #ifdef ISC_PLATFORM_USETHREADS @@ -111,16 +144,29 @@ struct isc_taskmgr { #endif /* ISC_PLATFORM_USETHREADS */ /* Locked by task manager lock. */ unsigned int default_quantum; - LIST(isc_task_t) tasks; - isc_tasklist_t ready_tasks; + LIST(isc__task_t) tasks; + isc__tasklist_t ready_tasks; + isc__tasklist_t ready_priority_tasks; + isc_taskmgrmode_t mode; #ifdef ISC_PLATFORM_USETHREADS isc_condition_t work_available; isc_condition_t exclusive_granted; + isc_condition_t paused; #endif /* ISC_PLATFORM_USETHREADS */ unsigned int tasks_running; + unsigned int tasks_ready; + isc_boolean_t pause_requested; isc_boolean_t exclusive_requested; isc_boolean_t exiting; -#ifndef ISC_PLATFORM_USETHREADS + + /* + * Multiple threads can read/write 'excl' at the same time, so we need + * to protect the access. We can't use 'lock' since isc_task_detach() + * will try to acquire it. + */ + isc_mutex_t excl_lock; + isc__task_t *excl; +#ifdef USE_SHARED_MANAGER unsigned int refs; #endif /* ISC_PLATFORM_USETHREADS */ }; @@ -129,19 +175,139 @@ struct isc_taskmgr { #define DEFAULT_DEFAULT_QUANTUM 5 #define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) -#ifndef ISC_PLATFORM_USETHREADS -static isc_taskmgr_t *taskmgr = NULL; -#endif /* ISC_PLATFORM_USETHREADS */ +#ifdef USE_SHARED_MANAGER +static isc__taskmgr_t *taskmgr = NULL; +#endif /* USE_SHARED_MANAGER */ + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ + +isc_result_t +isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, + isc_task_t **taskp); +void +isc__task_attach(isc_task_t *source0, isc_task_t **targetp); +void +isc__task_detach(isc_task_t **taskp); +void +isc__task_send(isc_task_t *task0, isc_event_t **eventp); +void +isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp); +unsigned int +isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag); +unsigned int +isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag); +isc_boolean_t +isc_task_purgeevent(isc_task_t *task0, isc_event_t *event); +unsigned int +isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, + isc_eventlist_t *events); +unsigned int +isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events); +isc_result_t +isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, + void *arg); +void +isc__task_shutdown(isc_task_t *task0); +void +isc__task_destroy(isc_task_t **taskp); +void +isc__task_setname(isc_task_t *task0, const char *name, void *tag); +const char * +isc__task_getname(isc_task_t *task0); +void * +isc__task_gettag(isc_task_t *task0); +void +isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t); +isc_result_t +isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp); +void +isc__taskmgr_destroy(isc_taskmgr_t **managerp); +void +isc_taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0); +isc_result_t +isc_taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp); +isc_result_t +isc__task_beginexclusive(isc_task_t *task); +void +isc__task_endexclusive(isc_task_t *task0); +void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv); +isc_boolean_t +isc__task_privilege(isc_task_t *task0); +void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode); +isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0); + +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager); + +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager); + +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task); + +static struct isc__taskmethods { + isc_taskmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *purgeevent, *unsendrange, *getname, *gettag, *getcurrenttime; +} taskmethods = { + { + isc__task_attach, + isc__task_detach, + isc__task_destroy, + isc__task_send, + isc__task_sendanddetach, + isc__task_unsend, + isc__task_onshutdown, + isc__task_shutdown, + isc__task_setname, + isc__task_purge, + isc__task_purgerange, + isc__task_beginexclusive, + isc__task_endexclusive, + isc__task_setprivilege, + isc__task_privilege + }, + (void *)isc_task_purgeevent, + (void *)isc__task_unsendrange, + (void *)isc__task_getname, + (void *)isc__task_gettag, + (void *)isc__task_getcurrenttime +}; + +static isc_taskmgrmethods_t taskmgrmethods = { + isc__taskmgr_destroy, + isc__taskmgr_setmode, + isc__taskmgr_mode, + isc__task_create, + isc_taskmgr_setexcltask, + isc_taskmgr_excltask +}; /*** *** Tasks. ***/ static void -task_finished(isc_task_t *task) { - isc_taskmgr_t *manager = task->manager; +task_finished(isc__task_t *task) { + isc__taskmgr_t *manager = task->manager; REQUIRE(EMPTY(task->events)); + REQUIRE(task->nevents == 0); REQUIRE(EMPTY(task->on_shutdown)); REQUIRE(task->references == 0); REQUIRE(task->state == task_state_done); @@ -150,7 +316,7 @@ task_finished(isc_task_t *task) { LOCK(&manager->lock); UNLINK(manager->tasks, task, link); -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS if (FINISHED(manager)) { /* * All tasks have completed and the @@ -160,19 +326,21 @@ task_finished(isc_task_t *task) { */ BROADCAST(&manager->work_available); } -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ UNLOCK(&manager->lock); DESTROYLOCK(&task->lock); - task->magic = 0; + task->common.impmagic = 0; + task->common.magic = 0; isc_mem_put(manager->mctx, task, sizeof(*task)); } isc_result_t -isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, - isc_task_t **taskp) +isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, + isc_task_t **taskp) { - isc_task_t *task; + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc__task_t *task; isc_boolean_t exiting; isc_result_t result; @@ -193,15 +361,15 @@ isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, task->references = 1; INIT_LIST(task->events); INIT_LIST(task->on_shutdown); + task->nevents = 0; 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); + INIT_LINK(task, ready_priority_link); exiting = ISC_FALSE; LOCK(&manager->lock); @@ -219,14 +387,17 @@ isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, return (ISC_R_SHUTTINGDOWN); } - task->magic = TASK_MAGIC; - *taskp = task; + 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_task_attach(isc_task_t *source, isc_task_t **targetp) { +isc__task_attach(isc_task_t *source0, isc_task_t **targetp) { + isc__task_t *source = (isc__task_t *)source0; /* * Attach *targetp to source. @@ -241,11 +412,11 @@ isc_task_attach(isc_task_t *source, isc_task_t **targetp) { source->references++; UNLOCK(&source->lock); - *targetp = source; + *targetp = (isc_task_t *)source; } static inline isc_boolean_t -task_shutdown(isc_task_t *task) { +task_shutdown(isc__task_t *task) { isc_boolean_t was_idle = ISC_FALSE; isc_event_t *event, *prev; @@ -266,6 +437,7 @@ task_shutdown(isc_task_t *task) { } INSIST(task->state == task_state_ready || task->state == task_state_running); + /* * Note that we post shutdown events LIFO. */ @@ -275,15 +447,24 @@ task_shutdown(isc_task_t *task) { prev = PREV(event, ev_link); DEQUEUE(task->on_shutdown, event, ev_link); ENQUEUE(task->events, event, ev_link); + task->nevents++; } } return (was_idle); } +/* + * 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; +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); @@ -291,17 +472,16 @@ task_ready(isc_task_t *task) { 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 */ - + 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); } static inline isc_boolean_t -task_detach(isc_task_t *task) { +task_detach(isc__task_t *task) { /* * Caller must be holding the task lock. @@ -330,8 +510,8 @@ task_detach(isc_task_t *task) { } void -isc_task_detach(isc_task_t **taskp) { - isc_task_t *task; +isc__task_detach(isc_task_t **taskp) { + isc__task_t *task; isc_boolean_t was_idle; /* @@ -339,7 +519,7 @@ isc_task_detach(isc_task_t **taskp) { */ REQUIRE(taskp != NULL); - task = *taskp; + task = (isc__task_t *)*taskp; REQUIRE(VALID_TASK(task)); XTRACE("isc_task_detach"); @@ -355,7 +535,7 @@ isc_task_detach(isc_task_t **taskp) { } static inline isc_boolean_t -task_send(isc_task_t *task, isc_event_t **eventp) { +task_send(isc__task_t *task, isc_event_t **eventp) { isc_boolean_t was_idle = ISC_FALSE; isc_event_t *event; @@ -368,6 +548,7 @@ task_send(isc_task_t *task, isc_event_t **eventp) { REQUIRE(event != NULL); REQUIRE(event->ev_type > 0); REQUIRE(task->state != task_state_done); + REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink)); XTRACE("task_send"); @@ -379,13 +560,15 @@ task_send(isc_task_t *task, isc_event_t **eventp) { INSIST(task->state == task_state_ready || task->state == task_state_running); ENQUEUE(task->events, event, ev_link); + task->nevents++; *eventp = NULL; return (was_idle); } void -isc_task_send(isc_task_t *task, isc_event_t **eventp) { +isc__task_send(isc_task_t *task0, isc_event_t **eventp) { + isc__task_t *task = (isc__task_t *)task0; isc_boolean_t was_idle; /* @@ -426,9 +609,9 @@ isc_task_send(isc_task_t *task, isc_event_t **eventp) { } void -isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { +isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { isc_boolean_t idle1, idle2; - isc_task_t *task; + isc__task_t *task; /* * Send '*event' to '*taskp' and then detach '*taskp' from its @@ -436,7 +619,7 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { */ REQUIRE(taskp != NULL); - task = *taskp; + task = (isc__task_t *)*taskp; REQUIRE(VALID_TASK(task)); XTRACE("isc_task_sendanddetach"); @@ -462,7 +645,7 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { #define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0) static unsigned int -dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first, +dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first, isc_eventtype_t last, void *tag, isc_eventlist_t *events, isc_boolean_t purging) { @@ -491,6 +674,7 @@ dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first, (tag == NULL || event->ev_tag == tag) && (!purging || PURGE_OK(event))) { DEQUEUE(task->events, event, ev_link); + task->nevents--; ENQUEUE(*events, event, ev_link); count++; } @@ -502,9 +686,10 @@ dequeue_events(isc_task_t *task, void *sender, isc_eventtype_t first, } unsigned int -isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, - isc_eventtype_t last, void *tag) +isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag) { + isc__task_t *task = (isc__task_t *)task0; unsigned int count; isc_eventlist_t events; isc_event_t *event, *next_event; @@ -533,8 +718,8 @@ isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, } unsigned int -isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, - void *tag) +isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag) { /* * Purge events from a task's event queue. @@ -542,11 +727,12 @@ isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, XTRACE("isc_task_purge"); - return (isc_task_purgerange(task, sender, type, type, tag)); + return (isc__task_purgerange(task, sender, type, type, tag)); } isc_boolean_t -isc_task_purgeevent(isc_task_t *task, isc_event_t *event) { +isc_task_purgeevent(isc_task_t *task0, isc_event_t *event) { + isc__task_t *task = (isc__task_t *)task0; isc_event_t *curr_event, *next_event; /* @@ -574,6 +760,7 @@ isc_task_purgeevent(isc_task_t *task, isc_event_t *event) { next_event = NEXT(curr_event, ev_link); if (curr_event == event && PURGE_OK(event)) { DEQUEUE(task->events, curr_event, ev_link); + task->nevents--; break; } } @@ -588,9 +775,9 @@ isc_task_purgeevent(isc_task_t *task, isc_event_t *event) { } unsigned int -isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, - isc_eventtype_t last, void *tag, - isc_eventlist_t *events) +isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, + isc_eventlist_t *events) { /* * Remove events from a task's event queue. @@ -598,13 +785,13 @@ isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, XTRACE("isc_task_unsendrange"); - return (dequeue_events(task, sender, first, last, tag, events, - ISC_FALSE)); + return (dequeue_events((isc__task_t *)task, sender, first, + last, tag, events, ISC_FALSE)); } unsigned int -isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, - void *tag, isc_eventlist_t *events) +isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events) { /* * Remove events from a task's event queue. @@ -612,13 +799,15 @@ isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, XTRACE("isc_task_unsend"); - return (dequeue_events(task, sender, type, type, tag, events, - ISC_FALSE)); + return (dequeue_events((isc__task_t *)task, sender, type, + type, tag, events, ISC_FALSE)); } isc_result_t -isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg) +isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, + void *arg) { + isc__task_t *task = (isc__task_t *)task0; isc_boolean_t disallowed = ISC_FALSE; isc_result_t result = ISC_R_SUCCESS; isc_event_t *event; @@ -655,7 +844,8 @@ isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, const void *arg) } void -isc_task_shutdown(isc_task_t *task) { +isc__task_shutdown(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; isc_boolean_t was_idle; /* @@ -673,7 +863,7 @@ isc_task_shutdown(isc_task_t *task) { } void -isc_task_destroy(isc_task_t **taskp) { +isc__task_destroy(isc_task_t **taskp) { /* * Destroy '*taskp'. @@ -686,7 +876,8 @@ isc_task_destroy(isc_task_t **taskp) { } void -isc_task_setname(isc_task_t *task, const char *name, void *tag) { +isc__task_setname(isc_task_t *task0, const char *name, void *tag) { + isc__task_t *task = (isc__task_t *)task0; /* * Name 'task'. @@ -694,51 +885,117 @@ isc_task_setname(isc_task_t *task, const char *name, void *tag) { REQUIRE(VALID_TASK(task)); -#ifdef ISC_TASK_NAMES LOCK(&task->lock); memset(task->name, 0, sizeof(task->name)); - strlcpy(task->name, name, sizeof(task->name)); + strncpy(task->name, name, sizeof(task->name) - 1); task->tag = tag; UNLOCK(&task->lock); -#else - UNUSED(name); - UNUSED(tag); -#endif - } const char * -isc_task_getname(isc_task_t *task) { +isc__task_getname(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + return (task->name); } void * -isc_task_gettag(isc_task_t *task) { +isc__task_gettag(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + return (task->tag); } void -isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) { +isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { + isc__task_t *task = (isc__task_t *)task0; + REQUIRE(VALID_TASK(task)); REQUIRE(t != NULL); LOCK(&task->lock); - *t = task->now; - UNLOCK(&task->lock); } /*** *** Task Manager. ***/ + +/* + * Return ISC_TRUE if the current ready list for the manager, which is + * either ready_tasks or the ready_priority_tasks, depending on whether + * the manager is currently in normal or privileged execution mode. + * + * Caller must hold the task manager lock. + */ +static inline isc_boolean_t +empty_readyq(isc__taskmgr_t *manager) { + isc__tasklist_t queue; + + if (manager->mode == isc_taskmgrmode_normal) + queue = manager->ready_tasks; + else + queue = manager->ready_priority_tasks; + + return (ISC_TF(EMPTY(queue))); +} + +/* + * Dequeue and return a pointer to the first task on the current ready + * list for the manager. + * If the task is privileged, dequeue it from the other ready list + * as well. + * + * Caller must hold the task manager lock. + */ +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager) { + isc__task_t *task; + + if (manager->mode == isc_taskmgrmode_normal) + task = HEAD(manager->ready_tasks); + else + task = HEAD(manager->ready_priority_tasks); + + if (task != NULL) { + DEQUEUE(manager->ready_tasks, task, ready_link); + if (ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + } + + return (task); +} + +/* + * Push 'task' onto the ready_tasks queue. If 'task' has the privilege + * flag set, then also push it onto the ready_priority_tasks queue. + * + * Caller must hold the task manager lock. + */ +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { + ENQUEUE(manager->ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + manager->tasks_ready++; +} + static void -dispatch(isc_taskmgr_t *manager) { - isc_task_t *task; -#ifndef ISC_PLATFORM_USETHREADS +dispatch(isc__taskmgr_t *manager) { + isc__task_t *task; +#ifndef USE_WORKER_THREADS unsigned int total_dispatch_count = 0; - isc_tasklist_t ready_tasks; -#endif /* ISC_PLATFORM_USETHREADS */ + isc__tasklist_t new_ready_tasks; + isc__tasklist_t new_priority_tasks; + unsigned int tasks_ready = 0; +#endif /* USE_WORKER_THREADS */ REQUIRE(VALID_MANAGER(manager)); @@ -792,23 +1049,27 @@ dispatch(isc_taskmgr_t *manager) { * unlocks. The while expression is always protected by the lock. */ -#ifndef ISC_PLATFORM_USETHREADS - ISC_LIST_INIT(ready_tasks); +#ifndef USE_WORKER_THREADS + ISC_LIST_INIT(new_ready_tasks); + ISC_LIST_INIT(new_priority_tasks); #endif LOCK(&manager->lock); + while (!FINISHED(manager)) { -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS /* * 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. + * + * If a pause has been requested, don't do any work + * until it's been released. */ - while ((EMPTY(manager->ready_tasks) || - manager->exclusive_requested) && - !FINISHED(manager)) - { + while ((empty_readyq(manager) || manager->pause_requested || + manager->exclusive_requested) && !FINISHED(manager)) + { XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_WAIT, "wait")); @@ -817,15 +1078,15 @@ dispatch(isc_taskmgr_t *manager) { ISC_MSGSET_TASK, ISC_MSG_AWAKE, "awake")); } -#else /* ISC_PLATFORM_USETHREADS */ +#else /* USE_WORKER_THREADS */ if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || - EMPTY(manager->ready_tasks)) + empty_readyq(manager)) break; -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_WORKING, "working")); - task = HEAD(manager->ready_tasks); + task = pop_readyq(manager); if (task != NULL) { unsigned int dispatch_count = 0; isc_boolean_t done = ISC_FALSE; @@ -840,7 +1101,7 @@ dispatch(isc_taskmgr_t *manager) { * 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_ready--; manager->tasks_running++; UNLOCK(&manager->lock); @@ -854,6 +1115,7 @@ dispatch(isc_taskmgr_t *manager) { if (!EMPTY(task->events)) { event = HEAD(task->events); DEQUEUE(task->events, event, ev_link); + task->nevents--; /* * Execute the event action. @@ -864,13 +1126,15 @@ dispatch(isc_taskmgr_t *manager) { "execute action")); if (event->ev_action != NULL) { UNLOCK(&task->lock); - (event->ev_action)(task,event); + (event->ev_action)( + (isc_task_t *)task, + event); LOCK(&task->lock); } dispatch_count++; -#ifndef ISC_PLATFORM_USETHREADS +#ifndef USE_WORKER_THREADS total_dispatch_count++; -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ } if (task->references == 0 && @@ -955,12 +1219,15 @@ dispatch(isc_taskmgr_t *manager) { LOCK(&manager->lock); manager->tasks_running--; -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS if (manager->exclusive_requested && manager->tasks_running == 1) { SIGNAL(&manager->exclusive_granted); + } else if (manager->pause_requested && + manager->tasks_running == 0) { + SIGNAL(&manager->paused); } -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ if (requeue) { /* * We know we're awake, so we don't have @@ -981,28 +1248,52 @@ dispatch(isc_taskmgr_t *manager) { * were usually nonempty, the 'optimization' * might even hurt rather than help. */ -#ifdef ISC_PLATFORM_USETHREADS - ENQUEUE(manager->ready_tasks, task, - ready_link); +#ifdef USE_WORKER_THREADS + push_readyq(manager, task); #else - ENQUEUE(ready_tasks, task, ready_link); + ENQUEUE(new_ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(new_priority_tasks, task, + ready_priority_link); + tasks_ready++; #endif } } + +#ifdef USE_WORKER_THREADS + /* + * If we are in privileged execution mode and there are no + * tasks remaining on the current ready queue, then + * we're stuck. Automatically drop privileges at that + * point and continue with the regular ready queue. + */ + if (manager->tasks_running == 0 && empty_readyq(manager)) { + manager->mode = isc_taskmgrmode_normal; + if (!empty_readyq(manager)) + BROADCAST(&manager->work_available); + } +#endif } -#ifndef ISC_PLATFORM_USETHREADS - ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link); + +#ifndef USE_WORKER_THREADS + ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); + ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, + ready_priority_link); + manager->tasks_ready += tasks_ready; + if (empty_readyq(manager)) + manager->mode = isc_taskmgrmode_normal; #endif + UNLOCK(&manager->lock); } -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS static isc_threadresult_t #ifdef _WIN32 WINAPI #endif run(void *uap) { - isc_taskmgr_t *manager = uap; + isc__taskmgr_t *manager = uap; XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_STARTING, "starting")); @@ -1012,33 +1303,44 @@ run(void *uap) { XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_EXITING, "exiting")); +#ifdef OPENSSL_LEAKS + ERR_remove_state(0); +#endif + return ((isc_threadresult_t)0); } -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ static void -manager_free(isc_taskmgr_t *manager) { +manager_free(isc__taskmgr_t *manager) { isc_mem_t *mctx; -#ifdef ISC_PLATFORM_USETHREADS - (void)isc_condition_destroy(&manager->exclusive_granted); +#ifdef USE_WORKER_THREADS + (void)isc_condition_destroy(&manager->exclusive_granted); (void)isc_condition_destroy(&manager->work_available); + (void)isc_condition_destroy(&manager->paused); isc_mem_free(manager->mctx, manager->threads); -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ DESTROYLOCK(&manager->lock); - manager->magic = 0; + DESTROYLOCK(&manager->excl_lock); + manager->common.impmagic = 0; + manager->common.magic = 0; mctx = manager->mctx; isc_mem_put(mctx, manager, sizeof(*manager)); isc_mem_detach(&mctx); + +#ifdef USE_SHARED_MANAGER + taskmgr = NULL; +#endif /* USE_SHARED_MANAGER */ } isc_result_t -isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, - unsigned int default_quantum, isc_taskmgr_t **managerp) +isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp) { isc_result_t result; unsigned int i, started = 0; - isc_taskmgr_t *manager; + isc__taskmgr_t *manager; /* * Create a new task manager. @@ -1047,28 +1349,39 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, REQUIRE(workers > 0); REQUIRE(managerp != NULL && *managerp == NULL); -#ifndef ISC_PLATFORM_USETHREADS +#ifndef USE_WORKER_THREADS UNUSED(i); UNUSED(started); - UNUSED(workers); +#endif +#ifdef USE_SHARED_MANAGER if (taskmgr != NULL) { + if (taskmgr->refs == 0) + return (ISC_R_SHUTTINGDOWN); taskmgr->refs++; - *managerp = taskmgr; + *managerp = (isc_taskmgr_t *)taskmgr; return (ISC_R_SUCCESS); } -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_SHARED_MANAGER */ manager = isc_mem_get(mctx, sizeof(*manager)); if (manager == NULL) return (ISC_R_NOMEMORY); - manager->magic = TASK_MANAGER_MAGIC; + manager->common.methods = &taskmgrmethods; + manager->common.impmagic = TASK_MANAGER_MAGIC; + manager->common.magic = ISCAPI_TASKMGR_MAGIC; + manager->mode = isc_taskmgrmode_normal; manager->mctx = NULL; result = isc_mutex_init(&manager->lock); if (result != ISC_R_SUCCESS) goto cleanup_mgr; + result = isc_mutex_init(&manager->excl_lock); + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&manager->lock); + goto cleanup_mgr; + } -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS manager->workers = 0; manager->threads = isc_mem_allocate(mctx, workers * sizeof(isc_thread_t)); @@ -1092,19 +1405,31 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, result = ISC_R_UNEXPECTED; goto cleanup_workavailable; } -#endif /* ISC_PLATFORM_USETHREADS */ + if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_exclusivegranted; + } +#endif /* USE_WORKER_THREADS */ if (default_quantum == 0) default_quantum = DEFAULT_DEFAULT_QUANTUM; manager->default_quantum = default_quantum; INIT_LIST(manager->tasks); INIT_LIST(manager->ready_tasks); + INIT_LIST(manager->ready_priority_tasks); manager->tasks_running = 0; + manager->tasks_ready = 0; manager->exclusive_requested = ISC_FALSE; + manager->pause_requested = ISC_FALSE; manager->exiting = ISC_FALSE; + manager->excl = NULL; isc_mem_attach(mctx, &manager->mctx); -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS LOCK(&manager->lock); /* * Start workers. @@ -1124,16 +1449,19 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, return (ISC_R_NOTHREADS); } isc_thread_setconcurrency(workers); -#else /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_WORKER_THREADS */ +#ifdef USE_SHARED_MANAGER manager->refs = 1; taskmgr = manager; -#endif /* ISC_PLATFORM_USETHREADS */ +#endif /* USE_SHARED_MANAGER */ - *managerp = manager; + *managerp = (isc_taskmgr_t *)manager; return (ISC_R_SUCCESS); -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS + cleanup_exclusivegranted: + (void)isc_condition_destroy(&manager->exclusive_granted); cleanup_workavailable: (void)isc_condition_destroy(&manager->work_available); cleanup_threads: @@ -1147,9 +1475,9 @@ isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, } void -isc_taskmgr_destroy(isc_taskmgr_t **managerp) { - isc_taskmgr_t *manager; - isc_task_t *task; +isc__taskmgr_destroy(isc_taskmgr_t **managerp) { + isc__taskmgr_t *manager; + isc__task_t *task; unsigned int i; /* @@ -1157,18 +1485,20 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { */ REQUIRE(managerp != NULL); - manager = *managerp; + manager = (isc__taskmgr_t *)*managerp; REQUIRE(VALID_MANAGER(manager)); -#ifndef ISC_PLATFORM_USETHREADS +#ifndef USE_WORKER_THREADS UNUSED(i); +#endif /* USE_WORKER_THREADS */ - if (manager->refs > 1) { - manager->refs--; +#ifdef USE_SHARED_MANAGER + manager->refs--; + if (manager->refs > 0) { *managerp = NULL; return; } -#endif /* ISC_PLATFORM_USETHREADS */ +#endif XTHREADTRACE("isc_taskmgr_destroy"); /* @@ -1180,6 +1510,14 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { */ /* + * Detach the exclusive task before acquiring the manager lock + */ + LOCK(&manager->excl_lock); + if (manager->excl != NULL) + isc__task_detach((isc_task_t **) &manager->excl); + UNLOCK(&manager->excl_lock); + + /* * 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. @@ -1197,6 +1535,11 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { manager->exiting = ISC_TRUE; /* + * If privileged mode was on, turn it off. + */ + manager->mode = isc_taskmgrmode_normal; + + /* * Post shutdown event(s) to every task (if they haven't already been * posted). */ @@ -1205,10 +1548,10 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { task = NEXT(task, link)) { LOCK(&task->lock); if (task_shutdown(task)) - ENQUEUE(manager->ready_tasks, task, ready_link); + push_readyq(manager, task); UNLOCK(&task->lock); } -#ifdef ISC_PLATFORM_USETHREADS +#ifdef USE_WORKER_THREADS /* * 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 @@ -1222,36 +1565,74 @@ isc_taskmgr_destroy(isc_taskmgr_t **managerp) { */ for (i = 0; i < manager->workers; i++) (void)isc_thread_join(manager->threads[i], NULL); -#else /* ISC_PLATFORM_USETHREADS */ +#else /* USE_WORKER_THREADS */ /* * Dispatch the shutdown events. */ UNLOCK(&manager->lock); - while (isc__taskmgr_ready()) - (void)isc__taskmgr_dispatch(); + while (isc__taskmgr_ready((isc_taskmgr_t *)manager)) + (void)isc__taskmgr_dispatch((isc_taskmgr_t *)manager); if (!ISC_LIST_EMPTY(manager->tasks)) isc_mem_printallactive(stderr); INSIST(ISC_LIST_EMPTY(manager->tasks)); -#endif /* ISC_PLATFORM_USETHREADS */ +#ifdef USE_SHARED_MANAGER + taskmgr = NULL; +#endif +#endif /* USE_WORKER_THREADS */ manager_free(manager); *managerp = NULL; } -#ifndef ISC_PLATFORM_USETHREADS +void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + manager->mode = mode; + UNLOCK(&manager->lock); +} + +isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_taskmgrmode_t mode; + LOCK(&manager->lock); + mode = manager->mode; + UNLOCK(&manager->lock); + return (mode); +} + +#ifndef USE_WORKER_THREADS isc_boolean_t -isc__taskmgr_ready(void) { - if (taskmgr == NULL) +isc__taskmgr_ready(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_boolean_t is_ready; + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = taskmgr; +#endif + if (manager == NULL) return (ISC_FALSE); - return (ISC_TF(!ISC_LIST_EMPTY(taskmgr->ready_tasks))); + + LOCK(&manager->lock); + is_ready = !empty_readyq(manager); + UNLOCK(&manager->lock); + + return (is_ready); } isc_result_t -isc__taskmgr_dispatch(void) { - isc_taskmgr_t *manager = taskmgr; +isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; - if (taskmgr == NULL) +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = taskmgr; +#endif + if (manager == NULL) return (ISC_R_NOTFOUND); dispatch(manager); @@ -1259,33 +1640,94 @@ isc__taskmgr_dispatch(void) { return (ISC_R_SUCCESS); } -#endif /* ISC_PLATFORM_USETHREADS */ +#else +void +isc__taskmgr_pause(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + manager->pause_requested = ISC_TRUE; + LOCK(&manager->lock); + while (manager->tasks_running > 0) { + WAIT(&manager->paused, &manager->lock); + } + UNLOCK(&manager->lock); +} + +void +isc__taskmgr_resume(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = ISC_FALSE; + BROADCAST(&manager->work_available); + } + UNLOCK(&manager->lock); +} +#endif /* USE_WORKER_THREADS */ + +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)); + LOCK(&mgr->excl_lock); + if (mgr->excl != NULL) + isc__task_detach((isc_task_t **) &mgr->excl); + isc__task_attach(task0, (isc_task_t **) &mgr->excl); + UNLOCK(&mgr->excl_lock); +} isc_result_t -isc_task_beginexclusive(isc_task_t *task) { -#ifdef ISC_PLATFORM_USETHREADS - isc_taskmgr_t *manager = task->manager; +isc_taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp) { + isc__taskmgr_t *mgr = (isc__taskmgr_t *) mgr0; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_MANAGER(mgr)); + REQUIRE(taskp != NULL && *taskp == NULL); + + LOCK(&mgr->excl_lock); + if (mgr->excl != NULL) + isc__task_attach((isc_task_t *) mgr->excl, taskp); + else + result = ISC_R_NOTFOUND; + UNLOCK(&mgr->excl_lock); + + return (result); +} + +isc_result_t +isc__task_beginexclusive(isc_task_t *task0) { +#ifdef USE_WORKER_THREADS + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + REQUIRE(task->state == task_state_running); + /* XXX: Require task == manager->excl? */ + LOCK(&manager->lock); if (manager->exclusive_requested) { - UNLOCK(&manager->lock); + UNLOCK(&manager->lock); return (ISC_R_LOCKBUSY); } manager->exclusive_requested = ISC_TRUE; while (manager->tasks_running > 1) { WAIT(&manager->exclusive_granted, &manager->lock); } - UNLOCK(&manager->lock); + UNLOCK(&manager->lock); #else - UNUSED(task); + UNUSED(task0); #endif return (ISC_R_SUCCESS); } void -isc_task_endexclusive(isc_task_t *task) { -#ifdef ISC_PLATFORM_USETHREADS - isc_taskmgr_t *manager = task->manager; +isc__task_endexclusive(isc_task_t *task0) { +#ifdef USE_WORKER_THREADS + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + REQUIRE(task->state == task_state_running); LOCK(&manager->lock); REQUIRE(manager->exclusive_requested); @@ -1293,6 +1735,564 @@ isc_task_endexclusive(isc_task_t *task) { BROADCAST(&manager->work_available); UNLOCK(&manager->lock); #else - UNUSED(task); + UNUSED(task0); #endif } + +void +isc__task_setprivilege(isc_task_t *task0, isc_boolean_t priv) { + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + isc_boolean_t oldpriv; + + LOCK(&task->lock); + oldpriv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + if (priv) + task->flags |= TASK_F_PRIVILEGED; + else + task->flags &= ~TASK_F_PRIVILEGED; + UNLOCK(&task->lock); + + if (priv == oldpriv) + return; + + LOCK(&manager->lock); + if (priv && ISC_LINK_LINKED(task, ready_link)) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + UNLOCK(&manager->lock); +} + +isc_boolean_t +isc__task_privilege(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + isc_boolean_t priv; + + LOCK(&task->lock); + priv = ISC_TF((task->flags & TASK_F_PRIVILEGED) != 0); + UNLOCK(&task->lock); + return (priv); +} + +isc_result_t +isc__task_register(void) { + return (isc_task_register(isc__taskmgr_create)); +} + +isc_boolean_t +isc_task_exiting(isc_task_t *t) { + isc__task_t *task = (isc__task_t *)t; + + REQUIRE(VALID_TASK(task)); + return (TASK_SHUTTINGDOWN(task)); +} + + +#ifdef HAVE_LIBXML2 +#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0) +int +isc_taskmgr_renderxml(isc_taskmgr_t *mgr0, xmlTextWriterPtr writer) { + isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0; + isc__task_t *task = NULL; + int xmlrc; + + LOCK(&mgr->lock); + + /* + * Write out the thread-model, and some details about each depending + * on which type is enabled. + */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model")); +#ifdef ISC_PLATFORM_USETHREADS + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded")); + TRY0(xmlTextWriterEndElement(writer)); /* type */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "worker-threads")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->workers)); + TRY0(xmlTextWriterEndElement(writer)); /* worker-threads */ +#else /* ISC_PLATFORM_USETHREADS */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "non-threaded")); + TRY0(xmlTextWriterEndElement(writer)); /* type */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->refs)); + TRY0(xmlTextWriterEndElement(writer)); /* references */ +#endif /* ISC_PLATFORM_USETHREADS */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + mgr->default_quantum)); + TRY0(xmlTextWriterEndElement(writer)); /* default-quantum */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_running)); + TRY0(xmlTextWriterEndElement(writer)); /* tasks-running */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-ready")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_ready)); + TRY0(xmlTextWriterEndElement(writer)); /* tasks-ready */ + + TRY0(xmlTextWriterEndElement(writer)); /* thread-model */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks")); + task = ISC_LIST_HEAD(mgr->tasks); + while (task != NULL) { + LOCK(&task->lock); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "task")); + + if (task->name[0] != 0) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", + task->name)); + TRY0(xmlTextWriterEndElement(writer)); /* name */ + } + + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->references)); + TRY0(xmlTextWriterEndElement(writer)); /* references */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); + TRY0(xmlTextWriterWriteFormatString(writer, "%p", task)); + TRY0(xmlTextWriterEndElement(writer)); /* id */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", + statenames[task->state])); + TRY0(xmlTextWriterEndElement(writer)); /* state */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->quantum)); + TRY0(xmlTextWriterEndElement(writer)); /* quantum */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "events")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->nevents)); + TRY0(xmlTextWriterEndElement(writer)); /* events */ + + TRY0(xmlTextWriterEndElement(writer)); + + UNLOCK(&task->lock); + task = ISC_LIST_NEXT(task, link); + } + TRY0(xmlTextWriterEndElement(writer)); /* tasks */ + + error: + if (task != NULL) + UNLOCK(&task->lock); + UNLOCK(&mgr->lock); + + return (xmlrc); +} +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON +#define CHECKMEM(m) do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY;\ + goto error;\ + } \ +} while(0) + +isc_result_t +isc_taskmgr_renderjson(isc_taskmgr_t *mgr0, json_object *tasks) { + isc_result_t result = ISC_R_SUCCESS; + isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0; + isc__task_t *task = NULL; + json_object *obj = NULL, *array = NULL, *taskobj = NULL; + + LOCK(&mgr->lock); + + /* + * Write out the thread-model, and some details about each depending + * on which type is enabled. + */ +#ifdef ISC_PLATFORM_USETHREADS + obj = json_object_new_string("threaded"); + CHECKMEM(obj); + json_object_object_add(tasks, "thread-model", obj); + + obj = json_object_new_int(mgr->workers); + CHECKMEM(obj); + json_object_object_add(tasks, "worker-threads", obj); +#else /* ISC_PLATFORM_USETHREADS */ + obj = json_object_new_string("non-threaded"); + CHECKMEM(obj); + json_object_object_add(tasks, "thread-model", obj); + + obj = json_object_new_int(mgr->refs); + CHECKMEM(obj); + json_object_object_add(tasks, "references", obj); +#endif /* ISC_PLATFORM_USETHREADS */ + + obj = json_object_new_int(mgr->default_quantum); + CHECKMEM(obj); + json_object_object_add(tasks, "default-quantum", obj); + + obj = json_object_new_int(mgr->tasks_running); + CHECKMEM(obj); + json_object_object_add(tasks, "tasks-running", obj); + + obj = json_object_new_int(mgr->tasks_ready); + CHECKMEM(obj); + json_object_object_add(tasks, "tasks-ready", obj); + + array = json_object_new_array(); + CHECKMEM(array); + + for (task = ISC_LIST_HEAD(mgr->tasks); + task != NULL; + task = ISC_LIST_NEXT(task, link)) + { + char buf[255]; + + LOCK(&task->lock); + + taskobj = json_object_new_object(); + CHECKMEM(taskobj); + json_object_array_add(array, taskobj); + + sprintf(buf, "%p", task); + obj = json_object_new_string(buf); + CHECKMEM(obj); + json_object_object_add(taskobj, "id", obj); + + if (task->name[0] != 0) { + obj = json_object_new_string(task->name); + CHECKMEM(obj); + json_object_object_add(taskobj, "name", obj); + } + + obj = json_object_new_int(task->references); + CHECKMEM(obj); + json_object_object_add(taskobj, "references", obj); + + obj = json_object_new_string(statenames[task->state]); + CHECKMEM(obj); + json_object_object_add(taskobj, "state", obj); + + obj = json_object_new_int(task->quantum); + CHECKMEM(obj); + json_object_object_add(taskobj, "quantum", obj); + + obj = json_object_new_int(task->nevents); + CHECKMEM(obj); + json_object_object_add(taskobj, "events", obj); + + UNLOCK(&task->lock); + } + + json_object_object_add(tasks, "tasks", array); + array = NULL; + result = ISC_R_SUCCESS; + + error: + if (array != NULL) + json_object_put(array); + + if (task != NULL) + UNLOCK(&task->lock); + UNLOCK(&mgr->lock); + + return (result); +} +#endif + + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; +static isc_taskmgrcreatefunc_t taskmgr_createfunc = NULL; + +static void +initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_task_register(isc_taskmgrcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (taskmgr_createfunc == NULL) + taskmgr_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_taskmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + unsigned int workers, unsigned int default_quantum, + isc_taskmgr_t **managerp) +{ + isc_result_t result; + + LOCK(&createlock); + + REQUIRE(taskmgr_createfunc != NULL); + result = (*taskmgr_createfunc)(mctx, workers, default_quantum, + managerp); + + UNLOCK(&createlock); + + if (result == ISC_R_SUCCESS) + isc_appctx_settaskmgr(actx, *managerp); + + return (result); +} + +isc_result_t +isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp) +{ + isc_result_t result; + + if (isc_bind9) + return (isc__taskmgr_create(mctx, workers, + default_quantum, managerp)); + LOCK(&createlock); + + REQUIRE(taskmgr_createfunc != NULL); + result = (*taskmgr_createfunc)(mctx, workers, default_quantum, + managerp); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_taskmgr_destroy(isc_taskmgr_t **managerp) { + REQUIRE(managerp != NULL && ISCAPI_TASKMGR_VALID(*managerp)); + + if (isc_bind9) + isc__taskmgr_destroy(managerp); + else + (*managerp)->methods->destroy(managerp); + + ENSURE(*managerp == NULL); +} + +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + if (isc_bind9) + isc__taskmgr_setmode(manager, mode); + else + manager->methods->setmode(manager, mode); +} + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + if (isc_bind9) + return (isc__taskmgr_mode(manager)); + + return (manager->methods->mode(manager)); +} + +isc_result_t +isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, + isc_task_t **taskp) +{ + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + REQUIRE(taskp != NULL && *taskp == NULL); + + if (isc_bind9) + return (isc__task_create(manager, quantum, taskp)); + + return (manager->methods->taskcreate(manager, quantum, taskp)); +} + +void +isc_task_attach(isc_task_t *source, isc_task_t **targetp) { + REQUIRE(ISCAPI_TASK_VALID(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + if (isc_bind9) + isc__task_attach(source, targetp); + else + source->methods->attach(source, targetp); + + ENSURE(*targetp == source); +} + +void +isc_task_detach(isc_task_t **taskp) { + REQUIRE(taskp != NULL && ISCAPI_TASK_VALID(*taskp)); + + if (isc_bind9) + isc__task_detach(taskp); + else + (*taskp)->methods->detach(taskp); + + ENSURE(*taskp == NULL); +} + +void +isc_task_send(isc_task_t *task, isc_event_t **eventp) { + REQUIRE(ISCAPI_TASK_VALID(task)); + REQUIRE(eventp != NULL && *eventp != NULL); + + if (isc_bind9) + isc__task_send(task, eventp); + else { + task->methods->send(task, eventp); + ENSURE(*eventp == NULL); + } +} + +void +isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { + REQUIRE(taskp != NULL && ISCAPI_TASK_VALID(*taskp)); + REQUIRE(eventp != NULL && *eventp != NULL); + + if (isc_bind9) + isc__task_sendanddetach(taskp, eventp); + else { + (*taskp)->methods->sendanddetach(taskp, eventp); + ENSURE(*eventp == NULL); + } + + ENSURE(*taskp == NULL); +} + +unsigned int +isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_unsend(task, sender, type, tag, events)); + + return (task->methods->unsend(task, sender, type, tag, events)); +} + +isc_result_t +isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_onshutdown(task, action, arg)); + + return (task->methods->onshutdown(task, action, arg)); +} + +void +isc_task_shutdown(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_shutdown(task); + else + task->methods->shutdown(task); +} + +void +isc_task_destroy(isc_task_t **taskp) { + if (!isc_bind9) + return; + + isc__task_destroy(taskp); +} + +void +isc_task_setname(isc_task_t *task, const char *name, void *tag) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_setname(task, name, tag); + else + task->methods->setname(task, name, tag); +} + +unsigned int +isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_purge(task, sender, type, tag)); + + return (task->methods->purgeevents(task, sender, type, tag)); +} + +isc_result_t +isc_task_beginexclusive(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_beginexclusive(task)); + + return (task->methods->beginexclusive(task)); +} + +void +isc_task_endexclusive(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_endexclusive(task); + else + task->methods->endexclusive(task); +} + +void +isc_task_setprivilege(isc_task_t *task, isc_boolean_t priv) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_setprivilege(task, priv); + else + task->methods->setprivilege(task, priv); +} + +isc_boolean_t +isc_task_privilege(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_privilege(task)); + + return (task->methods->privilege(task)); +} + +void +isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) { + if (!isc_bind9) + return; + + isc__task_getcurrenttime(task, t); +} + +/*% + * This is necessary for libisc's internal timer implementation. Other + * implementation might skip implementing this. + */ +unsigned int +isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_purgerange(task, sender, first, last, tag)); + + return (task->methods->purgerange(task, sender, first, last, tag)); +} |
