PQRST 1.2.1
Priority Queue for Running Simple Tasks
|
A Task represents a block of code to be executed at some point in the future. More...
#include <pqrst.h>
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 |
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
.
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.
|
inlinevirtual |
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.
other | the task which this task is being compared to |
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.
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.
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.
|
inline |
Retrieve the identification string.
This is available only if PQRST_TASK_IDENT
is defined.
bool Task::get_task_slippy | ( | void | ) | const |
|
inline |
Return true if the task is currently scheduled on a queue.
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
.
|
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()
.
t | the current time for this task |
Implemented in FunctionTask, and LoopTask.
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.
delay | the time after the reference time, at which the task is to be run |
new_task | the task to be queued (or the current task, if this is omitted or null) |
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.
next_time | the time at which the task is to be run |
new_task | the task to be queued (or the current task, if this is omitted or null) |
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
.
new_task | the task to be queued |
|
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.
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.
duration | the task's expected duration |
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.
task_priority | the set priority, either zero or one |
|
inline |
Set the identification string for a task.
This is available only if PQRST_TASK_IDENT
is defined.
ident_string | identification |
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.
is_slippy_p | true if the task should be made slippy |
|
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.
signal | the numeric signal to be passed on |
t | the effective time the signal was received |
|
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); }
delay | the interval after which this task should run |
|
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.
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
|
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.
Reimplemented in MaintenanceTask.
|
friend |
|
static |
Reports the PQRST version and compilation options.
This is in flash memory (Arduino PROGMEM
), so should be printed appropriately.