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