Tasks

A task implements an application behavior in response to an event. Tasks are executed to completion without pre-emption. Task start requests are queued if another task is already executing. A task start request when that task is already queued to start is effectively a null operation. However, a task start request for a task that is not queued but currently running will queue the task for a subsequent execution. This very simple task design is more than sufficient for the needs of the lamp application.

One key decision under such a task architecture is which task to run next if multiple tasks are queued. This code selects the task with the highest priority in such a case. Application code must be cognizant of this fact, as a perpetually queueing high priority task will prevent execution of a queued lower priority task. This behavior is similar to hardware interrupt dispatch in many processor architectures, so most developers should already be familiar with its pitfalls.

Task implementation

Tasks are implemented in the task.c, task.h and task_defs.h files, as found in the repository.

Task identifiers are defind in the taskdefs.h file. Each task id is an enumeration, with the highest priority task given the value of zero, and successively lower priority tasks having incrementally larger numbers. Since the task queue is implemented as bits in a variable, the width of the taskid_t structure defines the maximum number of supported tasks.

Before using, initialize the task subsystem:

task_init();

To queue a task for execution, post it. Tasks can be posted from within an ISR or from without. A task can post itself as well.

task_post(TASK_ID);

Task dispatch is constructed from the taskget() function, which atomically gets and clears the next task (bit). The user application code then constructs a switch statement or similar structure to call a function associated with each task id. The lamp code implements this behavior in the usertasks() function in main.c. Check it out in the repository. A short version:

void user_tasks(unsigned char block)
{
  task_id_t tid;

  while ((tid = task_get(block)) >= 0) {
    switch (tid) {
      case TASK_BTN_PB:
        pb_task();
        break;
      ...
    }
  }
}