task.c revision 2acf5526612dd7bd01b646c0941031437fb97d92
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *** General Macros.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We use macros instead of calling the os_ routines directly because
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * the capital letters make the locking stand out.
368b37b616234fce3d23099eb180f1dd38e1fb62Mark Andrews * We INSIST that they succeed since there's no way for us to continue
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * if they fail.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define LOCK(lp) INSIST(os_mutex_lock((lp)))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define UNLOCK(lp) INSIST(os_mutex_unlock((lp)))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define WAIT(cvp, lp) INSIST(os_condition_wait((cvp), (lp)))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define BROADCAST(cvp) INSIST(os_condition_broadcast((cvp)))
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define XTRACE(m) printf("%s %p\n", (m), os_thread_self())
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtontypedef enum {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington task_state_idle, task_state_ready, task_state_running,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* Not locked. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* Locked by task lock. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* Locked by task manager lock. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* Not locked. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington /* Locked by task manager lock. */
8804fd9936acd703073c4a75072852c38738a990Brian Wellington#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks))
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtonevent_allocate(mem_context_t mctx, task_eventtype_t type,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington task_action_t action, void *arg, size_t size)
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtontask_event_allocate(mem_context_t mctx, task_eventtype_t type,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington task_action_t action, void *arg, size_t size)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington return (event_allocate(mctx, type, action, arg, size));
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews * All tasks have completed and the
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * task manager is exiting. Wake up
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * any idle worker threads so they
8804fd9936acd703073c4a75072852c38738a990Brian Wellington mem_put(manager->mctx, task, sizeof *task);
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtontask_create(task_manager_t manager, task_action_t shutdown_action,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington void *shutdown_arg, unsigned int quantum, task_t *taskp)
8804fd9936acd703073c4a75072852c38738a990Brian Wellington task = mem_get(manager->mctx, sizeof *task);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington mem_put(manager->mctx, task, sizeof *task);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington task->shutdown_event = event_allocate(manager->mctx,
8804fd9936acd703073c4a75072852c38738a990Brian Wellington mem_put(manager->mctx, task, sizeof *task);
8804fd9936acd703073c4a75072852c38738a990Brian Wellingtontask_send_event(task_t task, task_event_t *eventp) {
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We're trying hard to hold locks for as short a time as possible.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We're also trying to hold as few locks as possible. This is why
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * some processing is deferred until after a lock is released.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We need to add this task to the ready queue.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We've waited until now to do it, rather than doing it
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * while holding the task lock, because we don't want to
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * block while holding the task lock.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We've changed the state to ready, so no one else will
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * be trying to add this task to the ready queue. It
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * thus doesn't matter if more events have been added to
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * the queue after we gave up the task lock.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Shutting down a task requires posting a shutdown event
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * to the task's queue and then executing it, so there's
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * no way the task can disappear. A task is always on the
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * task manager's 'tasks' list, so the task manager can
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * always post a shutdown event to all tasks if it is
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * requested to shutdown.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ENQUEUE(manager->ready_tasks, task, ready_link);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * If the runnable queue is empty, the worker threads could
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * either be executing tasks or waiting for something to do.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * We wakeup anyone who is sleeping.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * This routine is very similar to task_send_event() above.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ENQUEUE(task->events, task->shutdown_event, link);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington ENQUEUE(manager->ready_tasks, task, ready_link);
8804fd9936acd703073c4a75072852c38738a990Brian Wellington *** Task Manager.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Again we're trying to hold the lock for as short a time as possible
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * and to do as little locking and unlocking as possible.
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * In both while loops, the appropriate lock must be held before the
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * while body starts. Code which acquired the lock at the top of
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * the loop would be more readable, but would result in a lot of
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * extra locking. Compare:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * Straightforward:
8804fd9936acd703073c4a75072852c38738a990Brian Wellington * while (expression) {
unsigned int dispatch_count = 0;
while (!done) {
if (discard_remaining) {
if (free_task)
if (requeue) {
if (no_workers)
return (NULL);
unsigned int i, started = 0;
if (workers == 0)
if (default_quantum == 0)
for (i = 0; i < workers; i++) {
started++;
if (started == 0) {
return (started);