📄 task.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1998-2003 Internet Software Consortium. * * Permission to use, copy, modify, and 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. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: task.c,v 1.85.2.3.8.4 2004/03/08 21:06:29 marka Exp $ *//* * Principal Author: Bob Halley *//* * XXXRTH Need to document the states a task can be in, and the rules * for changing states. */#include <config.h>#include <isc/condition.h>#include <isc/event.h>#include <isc/magic.h>#include <isc/mem.h>#include <isc/msgs.h>#include <isc/platform.h>#include <isc/string.h>#include <isc/task.h>#include <isc/thread.h>#include <isc/util.h>#ifndef ISC_PLATFORM_USETHREADS#include "task_p.h"#endif /* ISC_PLATFORM_USETHREADS */#define ISC_TASK_NAMES 1#ifdef ISC_TASK_TRACE#define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ task, isc_thread_self(), (m))#define XTTRACE(t, m) fprintf(stderr, "task %p thread %lu: %s\n", \ (t), isc_thread_self(), (m))#define XTHREADTRACE(m) fprintf(stderr, "thread %lu: %s\n", \ isc_thread_self(), (m))#else#define XTRACE(m)#define XTTRACE(t, m)#define XTHREADTRACE(m)#endif/*** *** Types. ***/typedef enum { task_state_idle, task_state_ready, task_state_running, task_state_done} task_state_t;#define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K')#define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC)struct isc_task { /* Not locked. */ unsigned int magic; 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 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;};#define TASK_F_SHUTTINGDOWN 0x01#define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ != 0)#define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M')#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC)struct isc_taskmgr { /* Not locked. */ unsigned int magic; isc_mem_t * mctx; isc_mutex_t lock; unsigned int workers;#ifdef ISC_PLATFORM_USETHREADS isc_thread_t * threads;#endif /* ISC_PLATFORM_USETHREADS */ /* Locked by task manager lock. */ unsigned int default_quantum; LIST(isc_task_t) tasks; isc_tasklist_t ready_tasks;#ifdef ISC_PLATFORM_USETHREADS isc_condition_t work_available; isc_condition_t exclusive_granted;#endif /* ISC_PLATFORM_USETHREADS */ unsigned int tasks_running; isc_boolean_t exclusive_requested; isc_boolean_t exiting;#ifndef ISC_PLATFORM_USETHREADS unsigned int refs;#endif /* ISC_PLATFORM_USETHREADS */};#define DEFAULT_TASKMGR_QUANTUM 10#define DEFAULT_DEFAULT_QUANTUM 5#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))#ifndef ISC_PLATFORM_USETHREADSstatic isc_taskmgr_t *taskmgr = NULL;#endif /* ISC_PLATFORM_USETHREADS *//*** *** Tasks. ***/static voidtask_finished(isc_task_t *task) { isc_taskmgr_t *manager = task->manager; REQUIRE(EMPTY(task->events)); REQUIRE(EMPTY(task->on_shutdown)); REQUIRE(task->references == 0); REQUIRE(task->state == task_state_done); XTRACE("task_finished"); LOCK(&manager->lock); UNLINK(manager->tasks, task, link);#ifdef ISC_PLATFORM_USETHREADS if (FINISHED(manager)) { /* * All tasks have completed and the * task manager is exiting. Wake up * any idle worker threads so they * can exit. */ BROADCAST(&manager->work_available); }#endif /* ISC_PLATFORM_USETHREADS */ UNLOCK(&manager->lock); DESTROYLOCK(&task->lock); task->magic = 0; isc_mem_put(manager->mctx, task, sizeof(*task));}isc_result_tisc_task_create(isc_taskmgr_t *manager, unsigned int quantum, isc_task_t **taskp){ isc_task_t *task; isc_boolean_t exiting; REQUIRE(VALID_MANAGER(manager)); REQUIRE(taskp != NULL && *taskp == NULL); task = isc_mem_get(manager->mctx, sizeof(*task)); if (task == NULL) return (ISC_R_NOMEMORY); XTRACE("isc_task_create"); task->manager = manager; if (isc_mutex_init(&task->lock) != ISC_R_SUCCESS) { isc_mem_put(manager->mctx, task, sizeof(*task)); UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); return (ISC_R_UNEXPECTED); } task->state = task_state_idle; task->references = 1; INIT_LIST(task->events); INIT_LIST(task->on_shutdown); task->quantum = quantum; task->flags = 0; task->now = 0;#ifdef ISC_TASK_NAMES memset(task->name, 0, sizeof(task->name)); task->tag = NULL;#endif INIT_LINK(task, link); INIT_LINK(task, ready_link); exiting = ISC_FALSE; LOCK(&manager->lock); if (!manager->exiting) { if (task->quantum == 0) task->quantum = manager->default_quantum; APPEND(manager->tasks, task, link); } else exiting = ISC_TRUE; UNLOCK(&manager->lock); if (exiting) { DESTROYLOCK(&task->lock); isc_mem_put(manager->mctx, task, sizeof(*task)); return (ISC_R_SHUTTINGDOWN); } task->magic = TASK_MAGIC; *taskp = task; return (ISC_R_SUCCESS);}voidisc_task_attach(isc_task_t *source, isc_task_t **targetp) { /* * Attach *targetp to source. */ REQUIRE(VALID_TASK(source)); REQUIRE(targetp != NULL && *targetp == NULL); XTTRACE(source, "isc_task_attach"); LOCK(&source->lock); source->references++; UNLOCK(&source->lock); *targetp = source;}static inline isc_boolean_ttask_shutdown(isc_task_t *task) { isc_boolean_t was_idle = ISC_FALSE; isc_event_t *event, *prev; /* * Caller must be holding the task's lock. */ XTRACE("task_shutdown"); if (! TASK_SHUTTINGDOWN(task)) { XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_SHUTTINGDOWN, "shutting down")); task->flags |= TASK_F_SHUTTINGDOWN; if (task->state == task_state_idle) { INSIST(EMPTY(task->events)); task->state = task_state_ready; was_idle = ISC_TRUE; } INSIST(task->state == task_state_ready || task->state == task_state_running); /* * Note that we post shutdown events LIFO. */ for (event = TAIL(task->on_shutdown); event != NULL; event = prev) { prev = PREV(event, ev_link); DEQUEUE(task->on_shutdown, event, ev_link); ENQUEUE(task->events, event, ev_link); } } return (was_idle);}static inline voidtask_ready(isc_task_t *task) { isc_taskmgr_t *manager = task->manager; REQUIRE(VALID_MANAGER(manager)); REQUIRE(task->state == task_state_ready); XTRACE("task_ready"); LOCK(&manager->lock); ENQUEUE(manager->ready_tasks, task, ready_link);#ifdef ISC_PLATFORM_USETHREADS SIGNAL(&manager->work_available);#endif /* ISC_PLATFORM_USETHREADS */ UNLOCK(&manager->lock);}static inline isc_boolean_ttask_detach(isc_task_t *task) { /* * Caller must be holding the task lock. */ REQUIRE(task->references > 0); XTRACE("detach"); task->references--; if (task->references == 0 && task->state == task_state_idle) { INSIST(EMPTY(task->events)); /* * There are no references to this task, and no * pending events. We could try to optimize and * either initiate shutdown or clean up the task, * depending on its state, but it's easier to just * make the task ready and allow run() or the event * loop to deal with shutting down and termination. */ task->state = task_state_ready; return (ISC_TRUE); } return (ISC_FALSE);}voidisc_task_detach(isc_task_t **taskp) { isc_task_t *task; isc_boolean_t was_idle; /* * Detach *taskp from its task. */ REQUIRE(taskp != NULL); task = *taskp; REQUIRE(VALID_TASK(task)); XTRACE("isc_task_detach"); LOCK(&task->lock); was_idle = task_detach(task); UNLOCK(&task->lock); if (was_idle) task_ready(task); *taskp = NULL;}static inline isc_boolean_ttask_send(isc_task_t *task, isc_event_t **eventp) { isc_boolean_t was_idle = ISC_FALSE; isc_event_t *event; /* * Caller must be holding the task lock. */ REQUIRE(eventp != NULL); event = *eventp; REQUIRE(event != NULL); REQUIRE(event->ev_type > 0); REQUIRE(task->state != task_state_done); XTRACE("task_send"); if (task->state == task_state_idle) { was_idle = ISC_TRUE; INSIST(EMPTY(task->events)); task->state = task_state_ready; } INSIST(task->state == task_state_ready || task->state == task_state_running); ENQUEUE(task->events, event, ev_link); *eventp = NULL; return (was_idle);}voidisc_task_send(isc_task_t *task, isc_event_t **eventp) { isc_boolean_t was_idle; /* * Send '*event' to 'task'. */ REQUIRE(VALID_TASK(task)); XTRACE("isc_task_send"); /* * We're trying hard to hold locks for as short a time as possible. * We're also trying to hold as few locks as possible. This is why * some processing is deferred until after the lock is released. */ LOCK(&task->lock); was_idle = task_send(task, eventp); UNLOCK(&task->lock); if (was_idle) { /* * We need to add this task to the ready queue. * * We've waited until now to do it because making a task * ready requires locking the manager. If we tried to do * this while holding the task lock, we could deadlock. * * We've changed the state to ready, so no one else will * be trying to add this task to the ready queue. The * only way to leave the ready state is by executing the * task. It thus doesn't matter if events are added, * removed, or a shutdown is started in the interval * between the time we released the task lock, and the time * we add the task to the ready queue. */ task_ready(task); }}voidisc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { isc_boolean_t idle1, idle2; isc_task_t *task; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -