PQRST 1.2.1
Priority Queue for Running Simple Tasks
Public Member Functions | Static Public Attributes | Protected Member Functions | Friends | List of all members
Task Class Referenceabstract

A Task represents a block of code to be executed at some point in the future. More...

#include <pqrst.h>

Inheritance diagram for Task:
Inheritance graph
[legend]

Public Member Functions

 Task ()
 Construct a task. More...
 
virtual ~Task ()
 
virtual void start (ms_t delay)
 Start the task. More...
 
virtual void start (void)
 Start the task immediately. More...
 
virtual void run (ms_t t)=0
 Runs one step of the task. More...
 
ms_t cancel (void)
 Cancel this task. More...
 
ms_t uncancel (void)
 Uncancel a task. More...
 
void run_at (ms_t t, Task *T=nullptr)
 Push a new task onto the queue, due at a given time. More...
 
void run_after (ms_t t, Task *T=nullptr)
 Push a new Task onto the queue, due some (non-negative) interval after the queue's current reference time. More...
 
void run_immediately (Task *T=nullptr)
 Push a task onto the queue, due immediately. More...
 
bool before (const Task *) const
 Impose an ordering on tasks. More...
 
bool set_task_slippy (bool)
 Set a task to be ‘slippy’. More...
 
bool get_task_slippy (void) const
 Determine whether a task is ‘slippy’. More...
 
bool is_enqueued_p (void) const
 Return true if the task is currently scheduled on a queue. More...
 
ms_t ready_after (void) const
 Return the interval until the Task is next ready, relative to the queue reference time. More...
 
void set_priority (unsigned char)
 Set the priority of the task. More...
 
unsigned char get_priority (void) const
 Return the priority of the task. More...
 
void set_duration (ms_t)
 Set the duration of the task. More...
 
ms_t get_duration (void) const
 Return the task's expected duration. More...
 
virtual bool user_task_p (void) const
 True if this is a ‘user task’, as opposed to a maintenance task. More...
 
void set_task_ident (const char *ident_string)
 Set the identification string for a task. More...
 
const char * get_task_ident (void) const
 Retrieve the identification string. More...
 

Static Public Attributes

static const PROGMEM char *const pqrst_version
 Reports the PQRST version and compilation options. More...
 

Protected Member Functions

virtual void signal (int signal, ms_t t)
 Receive a signal. More...
 
virtual void run_task (ms_t)
 Run this task; this is the method which is actually called when a task is due to be run. More...
 

Friends

class TaskQueue
 

Detailed Description

A Task represents a block of code to be executed at some point in the future.

An object which is to be queued in this way should extend the Task class, and implement the run method.

If PQRST_SELF_QUEUE is zero (the default), then there is a single global queue, called Queue, on which tasks are queued by default. If PQRST_SELF_QUEUE is non-zero, then a Task must be associated with a TaskQueue when it is created, and the constructor of Task, and of all of its subclasses, takes an initial TaskQueue* pointer as a first argument. A Task can reschedule itself on this queue, or schedule other tasks there.

After the task is created, it must be started (that is, placed on to the queue) with a call to start, or possibly run_after.

Constructor & Destructor Documentation

◆ Task()

Task::Task ( void  )

Construct a task.

Note: if the preprocessor constant PQRST_SELF_QUEUE is non-zero (the default is zero), then the constructor takes an argument TaskQueue*, pointing to the queue which this task should be associated with. The same must be true of each of the subclasses of this class.

◆ ~Task()

virtual Task::~Task ( )
inlinevirtual

Member Function Documentation

◆ before()

bool Task::before ( const Task other) const

Impose an ordering on tasks.

The ordering respects the relative priority of the tasks, and their anticipated durations. If the tasks are of equal priority, then this task compares earlier iff its duration is shorter.

Parameters
otherthe task which this task is being compared to
Returns
true if this task should be ordered before the other task

◆ cancel()

ms_t Task::cancel ( void  )

Cancel this task.

The task will be removed from the queue.

It's OK for a task to cancel itself, and this is a way for a LoopTask to terminate the loop.

You may cancel a task which is not scheduled – this is a no-op.

Returns
the time at which the task would have been due, or zero if it was not queued

◆ get_duration()

ms_t Task::get_duration ( void  ) const

Return the task's expected duration.

This method is available only if PQRST_TASK_PRIORITIES is non-zero.

Returns
the duration in ms
See also
set_duration

◆ get_priority()

unsigned char Task::get_priority ( void  ) const

Return the priority of the task.

This method is available only if PQRST_TASK_PRIORITIES is non-zero.

Returns
1 if the task is normal (high) priority, 0 otherwise
See also
set_priority

◆ get_task_ident()

const char * Task::get_task_ident ( void  ) const
inline

Retrieve the identification string.

This is available only if PQRST_TASK_IDENT is defined.

Returns
the string
See also
set_task_ident

◆ get_task_slippy()

bool Task::get_task_slippy ( void  ) const

Determine whether a task is ‘slippy’.

Returns
true if the task is slippy
See also
set_task_slippy

◆ is_enqueued_p()

bool Task::is_enqueued_p ( void  ) const
inline

Return true if the task is currently scheduled on a queue.

Returns
true if enqueued

◆ ready_after()

ms_t Task::ready_after ( void  ) const

Return the interval until the Task is next ready, relative to the queue reference time.

This should be used only for debugging and checking and not, for example, for run_at calculations.

If the task is unqueued, return TIME_NEVER.

Returns
the interval until the next due time
See also
TaskQueue::next_due_after

◆ run()

virtual void Task::run ( ms_t  t)
pure virtual

Runs one step of the task.

The task has the option of re-queuing a task on the queue it was retrieved from; it might re-queue itself or another task, with run_after(ms_t,Task*).

The run method is given the current time, as passed to the TaskQueue::run_ready method. This may be different from (later than) the time at which the task was due, if there was any delay in running the queue. The time at which the task was due becomes the queue's ‘reference time’ (see TaskQueue).

The task should regard the ‘current time’ as the time passed to it in the argument, and should typically not examine the time by calls to millis() or micros().

Parameters
tthe current time for this task
See also
set_task_slippy

Implemented in FunctionTask, and LoopTask.

◆ run_after()

void Task::run_after ( ms_t  delay,
Task new_task = nullptr 
)

Push a new Task onto the queue, due some (non-negative) interval after the queue's current reference time.

Thus this is equivalent to run_at(ms_t,Task*) when given a time delay ms after the current reference time .

Calling run_after(0) schedules the current task ‘now’. See TaskQueue::push_after for discussion.

Do not use this method outside a Task::run method.

When the preprocessor define PQRST_SELF_QUEUE is zero (the default), this task is queued on the single default queue; when it is non-zero, it is queued on the queue specified in the task's constructor.

Parameters
delaythe time after the reference time, at which the task is to be run
new_taskthe task to be queued (or the current task, if this is omitted or null)
See also
run_at
TaskQueue::push_after

◆ run_at()

void Task::run_at ( ms_t  next_time,
Task new_task = nullptr 
)

Push a new task onto the queue, due at a given time.

If the new_task is omitted, this reschedules the current task. See TaskQueue::push_after for the interpretation of the due time, and the run method for discussion of the reference time.

You should generally use run_after in preference to using this function; see TaskQueue::push_after for discussion.

Do not schedule events in the past. This will generally work much as expected, but it is not guaranteed to work correctly near the time the ms_t counter overflows. In particular, do not use run_at(0) as a way of running immediately; see TaskQueue::push_at for discussion.

Parameters
next_timethe time at which the task is to be run
new_taskthe task to be queued (or the current task, if this is omitted or null)
See also
TaskQueue::push_after

◆ run_immediately()

void Task::run_immediately ( Task new_task = nullptr)

Push a task onto the queue, due immediately.

This is broadly equivalent to run_after(ms_t,Task*) with a zero delay argument (so that it will be due at the queue's ‘reference time’), but see TaskQueue::push_immediately.

Parameters
new_taskthe task to be queued
See also
TaskQueue::push_immediately

◆ run_task()

void Task::run_task ( ms_t  t)
protectedvirtual

Run this task; this is the method which is actually called when a task is due to be run.

This calls the run method, possibly doing some class-specific housekeeping as it does so.

In some circumstances, it may be necessary for a subclass of Task to override this. For example, the LoopTask::run_task method decrements a counter before calling its parent's run_task() method. This means that implementations of run() need not worry about running superclass methods.

Reimplemented in LoopTask.

◆ set_duration()

void Task::set_duration ( ms_t  duration)

Set the duration of the task.

This is only an estimate. There is a maximum duration of (currently) 1023ms.

This method is available only if PQRST_TASK_PRIORITIES is non-zero.

Parameters
durationthe task's expected duration
See also
set_priority
get_duration

◆ set_priority()

void Task::set_priority ( unsigned char  task_priority)

Set the priority of the task.

A task can (currently) only have one of two priorities. Any priority above 1 is taken to be 1 (which is higher priority than 0).

We wish to avoid the situation where a long running ‘background’ task (a number of ms) is still running when a high-priority task is scheduled. If, therefore, when we add a task to the queue, we would end up with a high-priority task scheduled after a low-priority task with a duration which extends past the high-priority start-time, then we enqueue them so that the high-priority task is run (at its due time) before the low-priority one.

Two equal-priority tasks are scheduled with the shorter going first, or the first to be added going first if their durations are equal.

A low-priority, ‘long’, task should have a duration declared, and be marked as a non-priority task, with set_priority(0); the duration need only be an estimate for these scheduling purposes.

The algorithm is relatively simple, but should suffice. The goal is to resolve occasional collisions between tasks, and the algorithm doesn't attempt to resolve three-way collisions where, for example, a high-priority task ends up enqueued behind a task which might be in turn delayed by a previous long-running task. At that point, you have other problems.

By default, tasks are created as priority/non-background tasks of duration 1.

This method, and the associated priority-sensitive queue ordering, is available only if PQRST_TASK_PRIORITIES is non-zero.

Parameters
task_prioritythe set priority, either zero or one
See also
set_duration
get_priority

◆ set_task_ident()

void Task::set_task_ident ( const char *  ident_string)
inline

Set the identification string for a task.

This is available only if PQRST_TASK_IDENT is defined.

Parameters
ident_stringidentification
See also
get_task_ident

◆ set_task_slippy()

bool Task::set_task_slippy ( bool  is_slippy_p)

Set a task to be ‘slippy’.

Normally, a task is rescheduled based on the time at which it was due – this means that a repeating task will not slip if it is occasionally slightly delayed in starting. If this is undesirable behaviour – if we want a possibly expensive task to be rescheduled based on the actual time – then we can set it as a ‘slippy’ task.

If a non-slippy task takes a significant amount of time, or if it is appreciably delayed, then it may end up putting itself back on the queue, due in the past. In this case it will be immediately due. This can result in a pile-up of tasks, possibly uselessly, and possibly overwhelming the processor.

Parameters
is_slippy_ptrue if the task should be made slippy
Returns
the previous value of this parameter
See also
get_task_slippy

◆ signal()

void Task::signal ( int  signal,
ms_t  t 
)
protectedvirtual

Receive a signal.

This is a protected method, which should be overridden by an implementation if it wishes to take action on signals.

The task queue can be given a signal to pass on to enqueued tasks. The queue then runs through the currently waiting tasks and invokes this method for each one. The default method does nothing, so a task should override this method only if it wishes to take action in this case.

Parameters
signalthe numeric signal to be passed on
tthe effective time the signal was received
See also
TaskQueue::signal

◆ start() [1/2]

void Task::start ( ms_t  delay)
virtual

Start the task.

The task will first become due the given delay after now. If this is omitted, then the task will become immediately due. See TaskQueue::push_at for the interpretation of the due time.

The default implementation of this method consists of queuing the task to run after the given delay; that is, it is broadly equivalent to run_after(delay), though possibly clearer in intent. If, however, there is any work that the task should do on its first run, that can be neatly achieved by overriding this task. In such a case, it is likely that the overriding method will want to schedule the task with TaskQueue::push_after, or Task::run_after or, most straightforwardly, the parent Task::start method. Thus:

void MyClass::start(ms_t delay)
{
    // do one-time stuff...

    Task::start(delay);
}
Parameters
delaythe interval after which this task should run

◆ start() [2/2]

void Task::start ( void  )
virtual

Start the task immediately.

This is equivalent to Task::start(ms_t) with an argument of 0. This is the most common way that tasks are started.

We declare this as a separate method start(void), rather than using a default for the argument, so that it's possible for subclasses to override start(void) rather than obliging them to override start(ms_t) even when they will always ignore the argument.

◆ uncancel()

ms_t Task::uncancel ( void  )

Uncancel a task.

After a task has been removed from the queue using cancel, it may be put back on the queue with this method, and it will again be due at the time it was last scheduled. If that time was in the past – ie, the task is now overdue – it will be due immediately.

If the task is currently scheduled (ie, it has not been cancelled), this method is a no-op.

It is currently unspecified what the method returns if the task is now overdue

Returns
the new ready-time, if the task is ready in the future

◆ user_task_p()

virtual bool Task::user_task_p ( void  ) const
inlinevirtual

True if this is a ‘user task’, as opposed to a maintenance task.

This is not likely to be generally useful to user code, other than perhaps in a queue-traversal callback.

Returns
true if this is a user task

Reimplemented in MaintenanceTask.

Friends And Related Function Documentation

◆ TaskQueue

friend class TaskQueue
friend

Member Data Documentation

◆ pqrst_version

const PROGMEM char* const Task::pqrst_version
static

Reports the PQRST version and compilation options.

This is in flash memory (Arduino PROGMEM), so should be printed appropriately.


The documentation for this class was generated from the following files: