📄 task.c
字号:
} dispatch_count++;#ifndef ISC_PLATFORM_USETHREADS total_dispatch_count++;#endif /* ISC_PLATFORM_USETHREADS */ } if (task->references == 0 && EMPTY(task->events) && !TASK_SHUTTINGDOWN(task)) { isc_boolean_t was_idle; /* * There are no references and no * pending events for this task, * which means it will not become * runnable again via an external * action (such as sending an event * or detaching). * * We initiate shutdown to prevent * it from becoming a zombie. * * We do this here instead of in * the "if EMPTY(task->events)" block * below because: * * If we post no shutdown events, * we want the task to finish. * * If we did post shutdown events, * will still want the task's * quantum to be applied. */ was_idle = task_shutdown(task); INSIST(!was_idle); } if (EMPTY(task->events)) { /* * Nothing else to do for this task * right now. */ XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_EMPTY, "empty")); if (task->references == 0 && TASK_SHUTTINGDOWN(task)) { /* * The task is done. */ XTRACE(isc_msgcat_get( isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_DONE, "done")); finished = ISC_TRUE; task->state = task_state_done; } else task->state = task_state_idle; done = ISC_TRUE; } else if (dispatch_count >= task->quantum) { /* * Our quantum has expired, but * there is more work to be done. * We'll requeue it to the ready * queue later. * * We don't check quantum until * dispatching at least one event, * so the minimum quantum is one. */ XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, ISC_MSG_QUANTUM, "quantum")); task->state = task_state_ready; requeue = ISC_TRUE; done = ISC_TRUE; } } while (!done); UNLOCK(&task->lock); if (finished) task_finished(task); LOCK(&manager->lock); manager->tasks_running--;#ifdef ISC_PLATFORM_USETHREADS if (manager->exclusive_requested && manager->tasks_running == 1) { SIGNAL(&manager->exclusive_granted); }#endif /* ISC_PLATFORM_USETHREADS */ if (requeue) { /* * We know we're awake, so we don't have * to wakeup any sleeping threads if the * ready queue is empty before we requeue. * * A possible optimization if the queue is * empty is to 'goto' the 'if (task != NULL)' * block, avoiding the ENQUEUE of the task * and the subsequent immediate DEQUEUE * (since it is the only executable task). * We don't do this because then we'd be * skipping the exit_requested check. The * cost of ENQUEUE is low anyway, especially * when you consider that we'd have to do * an extra EMPTY check to see if we could * do the optimization. If the ready queue * were usually nonempty, the 'optimization' * might even hurt rather than help. */#ifdef ISC_PLATFORM_USETHREADS ENQUEUE(manager->ready_tasks, task, ready_link);#else ENQUEUE(ready_tasks, task, ready_link);#endif } } }#ifndef ISC_PLATFORM_USETHREADS ISC_LIST_APPENDLIST(manager->ready_tasks, ready_tasks, ready_link);#endif UNLOCK(&manager->lock);}#ifdef ISC_PLATFORM_USETHREADSstatic isc_threadresult_t#ifdef _WIN32WINAPI#endifrun(void *uap) { isc_taskmgr_t *manager = uap; XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_STARTING, "starting")); dispatch(manager); XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_EXITING, "exiting")); return ((isc_threadresult_t)0);}#endif /* ISC_PLATFORM_USETHREADS */static voidmanager_free(isc_taskmgr_t *manager) { isc_mem_t *mctx;#ifdef ISC_PLATFORM_USETHREADS (void)isc_condition_destroy(&manager->exclusive_granted); (void)isc_condition_destroy(&manager->work_available); isc_mem_put(manager->mctx, manager->threads, manager->workers * sizeof(isc_thread_t));#endif /* ISC_PLATFORM_USETHREADS */ DESTROYLOCK(&manager->lock); manager->magic = 0; mctx = manager->mctx; isc_mem_put(mctx, manager, sizeof(*manager)); isc_mem_detach(&mctx);}isc_result_tisc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, unsigned int default_quantum, isc_taskmgr_t **managerp){ isc_result_t result; unsigned int i, started = 0; isc_taskmgr_t *manager; /* * Create a new task manager. */ REQUIRE(workers > 0); REQUIRE(managerp != NULL && *managerp == NULL);#ifndef ISC_PLATFORM_USETHREADS UNUSED(i); UNUSED(started); UNUSED(workers); if (taskmgr != NULL) { taskmgr->refs++; *managerp = taskmgr; return (ISC_R_SUCCESS); }#endif /* ISC_PLATFORM_USETHREADS */ manager = isc_mem_get(mctx, sizeof(*manager)); if (manager == NULL) return (ISC_R_NOMEMORY); manager->magic = TASK_MANAGER_MAGIC; manager->mctx = NULL; manager->workers = 0; if (isc_mutex_init(&manager->lock) != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_mutex_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); result = ISC_R_UNEXPECTED; goto cleanup_mgr; }#ifdef ISC_PLATFORM_USETHREADS manager->threads = isc_mem_get(mctx, workers * sizeof(isc_thread_t)); if (manager->threads == NULL) { result = ISC_R_NOMEMORY; goto cleanup_lock; } if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_condition_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); result = ISC_R_UNEXPECTED; goto cleanup_threads; } if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) { UNEXPECTED_ERROR(__FILE__, __LINE__, "isc_condition_init() %s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ISC_MSG_FAILED, "failed")); result = ISC_R_UNEXPECTED; goto cleanup_workavailable; }#endif /* ISC_PLATFORM_USETHREADS */ if (default_quantum == 0) default_quantum = DEFAULT_DEFAULT_QUANTUM; manager->default_quantum = default_quantum; INIT_LIST(manager->tasks); INIT_LIST(manager->ready_tasks); manager->tasks_running = 0; manager->exclusive_requested = ISC_FALSE; manager->exiting = ISC_FALSE; manager->workers = 0; isc_mem_attach(mctx, &manager->mctx);#ifdef ISC_PLATFORM_USETHREADS LOCK(&manager->lock); /* * Start workers. */ for (i = 0; i < workers; i++) { if (isc_thread_create(run, manager, &manager->threads[manager->workers]) == ISC_R_SUCCESS) { manager->workers++; started++; } } UNLOCK(&manager->lock); if (started == 0) { manager_free(manager); return (ISC_R_NOTHREADS); } isc_thread_setconcurrency(workers);#else /* ISC_PLATFORM_USETHREADS */ manager->refs = 1; taskmgr = manager;#endif /* ISC_PLATFORM_USETHREADS */ *managerp = manager; return (ISC_R_SUCCESS);#ifdef ISC_PLATFORM_USETHREADS cleanup_workavailable: (void)isc_condition_destroy(&manager->work_available); cleanup_threads: isc_mem_put(mctx, manager->threads, workers * sizeof(isc_thread_t)); cleanup_lock: DESTROYLOCK(&manager->lock);#endif cleanup_mgr: isc_mem_put(mctx, manager, sizeof(*manager)); return (result);}voidisc_taskmgr_destroy(isc_taskmgr_t **managerp) { isc_taskmgr_t *manager; isc_task_t *task; unsigned int i; /* * Destroy '*managerp'. */ REQUIRE(managerp != NULL); manager = *managerp; REQUIRE(VALID_MANAGER(manager));#ifndef ISC_PLATFORM_USETHREADS UNUSED(i); if (manager->refs > 1) { manager->refs--; *managerp = NULL; return; }#endif /* ISC_PLATFORM_USETHREADS */ XTHREADTRACE("isc_taskmgr_destroy"); /* * Only one non-worker thread may ever call this routine. * If a worker thread wants to initiate shutdown of the * task manager, it should ask some non-worker thread to call * isc_taskmgr_destroy(), e.g. by signalling a condition variable * that the startup thread is sleeping on. */ /* * Unlike elsewhere, we're going to hold this lock a long time. * We need to do so, because otherwise the list of tasks could * change while we were traversing it. * * This is also the only function where we will hold both the * task manager lock and a task lock at the same time. */ LOCK(&manager->lock); /* * Make sure we only get called once. */ INSIST(!manager->exiting); manager->exiting = ISC_TRUE; /* * Post shutdown event(s) to every task (if they haven't already been * posted). */ for (task = HEAD(manager->tasks); task != NULL; task = NEXT(task, link)) { LOCK(&task->lock); if (task_shutdown(task)) ENQUEUE(manager->ready_tasks, task, ready_link); UNLOCK(&task->lock); }#ifdef ISC_PLATFORM_USETHREADS /* * Wake up any sleeping workers. This ensures we get work done if * there's work left to do, and if there are already no tasks left * it will cause the workers to see manager->exiting. */ BROADCAST(&manager->work_available); UNLOCK(&manager->lock); /* * Wait for all the worker threads to exit. */ for (i = 0; i < manager->workers; i++) (void)isc_thread_join(manager->threads[i], NULL);#else /* ISC_PLATFORM_USETHREADS */ /* * Dispatch the shutdown events. */ UNLOCK(&manager->lock); while (isc__taskmgr_ready()) (void)isc__taskmgr_dispatch(); INSIST(ISC_LIST_EMPTY(manager->tasks));#endif /* ISC_PLATFORM_USETHREADS */ manager_free(manager); *managerp = NULL;}#ifndef ISC_PLATFORM_USETHREADSisc_boolean_tisc__taskmgr_ready(void) { if (taskmgr == NULL) return (ISC_FALSE); return (ISC_TF(!ISC_LIST_EMPTY(taskmgr->ready_tasks)));}isc_result_tisc__taskmgr_dispatch(void) { isc_taskmgr_t *manager = taskmgr; if (taskmgr == NULL) return (ISC_R_NOTFOUND); dispatch(manager); return (ISC_R_SUCCESS);}#endif /* ISC_PLATFORM_USETHREADS */isc_result_tisc_task_beginexclusive(isc_task_t *task) {#ifdef ISC_PLATFORM_USETHREADS isc_taskmgr_t *manager = task->manager; REQUIRE(task->state == task_state_running); LOCK(&manager->lock); if (manager->exclusive_requested) { UNLOCK(&manager->lock); return (ISC_R_LOCKBUSY); } manager->exclusive_requested = ISC_TRUE; while (manager->tasks_running > 1) { WAIT(&manager->exclusive_granted, &manager->lock); } UNLOCK(&manager->lock); #else UNUSED(task);#endif return (ISC_R_SUCCESS);}voidisc_task_endexclusive(isc_task_t *task) {#ifdef ISC_PLATFORM_USETHREADS isc_taskmgr_t *manager = task->manager; REQUIRE(task->state == task_state_running); LOCK(&manager->lock); REQUIRE(manager->exclusive_requested); manager->exclusive_requested = ISC_FALSE; BROADCAST(&manager->work_available); UNLOCK(&manager->lock);#else UNUSED(task);#endif}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -