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