PQRST  1.1
Priority Queue for Running Simple Tasks
Public Member Functions | Friends | List of all members
TaskQueue Class Reference

A priority queue of Tasks. More...

#include <pqrst.h>

Public Member Functions

 TaskQueue (void)
 Creates a new TaskQueue. More...
 
bool is_empty (void) const
 Return true if the task queue is empty. More...
 
unsigned int size (bool inc_maintenance=false) const
 Return the number of user tasks currently in the queue. More...
 
void traverse_queue (traverse_queue_cb_t, int max=-1)
 Traverse the queue. More...
 
void dump_queue_contents (void)
 Dump the queue. More...
 
void initialise (ms_t start_reftime=0)
 Initialise, or reinitialise, a TaskQueue. More...
 
bool run_ready (ms_t)
 If the head task in the queue is ready at the given time (ie, its due time is at or before that time), then run it. More...
 
ms_t next_due_after (void) const
 Return the interval between the queue reference time and the due-time of the task at the head of the queue. More...
 
void remove (Task *)
 Remove from the queue an element which is == to the given Task. More...
 
void remove_all_tasks (void)
 Discard all of the tasks from the queue, without running them. More...
 
void signal (int, ms_t)
 Pass the given integer on to each task in the queue. More...
 
void push_at (Task *, ms_t)
 Push the given task onto the queue, due at time t. More...
 
void push_after (Task *, ms_t)
 Push the given task onto the queue, due some (non-negative) interval after the current reference time. More...
 
void push_immediately (Task *)
 Push the given task on the queue, due immediately (in particular, at the current reference time). More...
 
int lateness (ms_t) const
 Return the interval between the given time and the queue's ‘reference time’. More...
 

Friends

bool Task::before (const Task *other) const
 
ms_t Task::ready_after (void) const
 

Detailed Description

A priority queue of Tasks.

The queue is maintained in time order; if PQRST_TASK_PRIORITIES is defined as 1, it additionally takes account of a high/low priority task flag.

Tasks are added to the queue with a ‘ready time’ which is either absolute, or relative to the queue's current ‘reference time’. This reference time is notionally ‘now’, but it is set to be a task's ‘ready time’ when a task is run. It may therefore be slightly different from the ‘current time’ value passed to a Task::run method. This also means that two tasks which have the same ready time will have the same reference time when run. It is this reference time which is used when calculating offsets using TaskQueue::push_after and Task::run_after.

It may not need saying, but scheduling tasks within an interrupt handler is probably an extremely bad idea (at least in this version of the code).

Note: the reference time, as a reference for rescheduling, is reliable only within the Task::run method, and so the push_after and Task::run_after methods should only be called within an implementation of that method.

Constructor & Destructor Documentation

◆ TaskQueue()

TaskQueue::TaskQueue ( void  )

Creates a new TaskQueue.

Member Function Documentation

◆ dump_queue_contents()

void TaskQueue::dump_queue_contents ( void  )

Dump the queue.

The output is a sequence of times and pointers to functions. For debugging only, since the printf will do nothing useful otherwise. This method is therefore not available unless the variable PQRST_TEST_MODE is set. The method traverse_queue is declared, though, so some debugging is possible in that case.

◆ initialise()

void TaskQueue::initialise ( ms_t  start_reftime = 0)

Initialise, or reinitialise, a TaskQueue.

This is generally not necessary, as it is done within the TaskQueue constructor.

It is necessary to reinitialise a Queue if we want to start using it again after it has been cleared by remove_all_tasks, or if we first start using a queue some significant time after the program has started.

This is necessary because the queue is initialised by default with a ‘reference time’ of zero, and any tasks queued, with Task::run_after, before the first call to run_ready will be queued relative to this. If this is significantly different from the first time value passed to run_ready, then non-slippy tasks will be requeued at the wrong time, possibly reoeatedly, until they ‘catch up’ with the current time as passed to run_after.

Parameters
start_reftimethe current time
Since
start_reftime argument added after v1.0

◆ is_empty()

bool TaskQueue::is_empty ( void  ) const

Return true if the task queue is empty.

◆ lateness()

int TaskQueue::lateness ( ms_t  t) const

Return the interval between the given time and the queue's ‘reference time’.

Note: the current implementation of this method assumes that t is not earlier than the reference time, and it will not give particularly useful answers for times which are earlier. A future implementation of this method may change the semantics in this case.

Parameters
tthe time of interest
Returns
how late we are behind the queue reference time

◆ next_due_after()

ms_t TaskQueue::next_due_after ( void  ) const

Return the interval between the queue reference time and the due-time of the task at the head of the queue.

If the head task is overdue, this returns zero, rather than a negative value. Returns TIME_NEVER if the queue is empty.

Returns
the interval
See also
Task::ready_after

◆ push_after()

void TaskQueue::push_after ( Task T,
ms_t  interval 
)

Push the given task onto the queue, due some (non-negative) interval after the current reference time.

If the task is already present on the queue, then the earlier scheduling is cancelled. A task will only be scheduled on a queue once (it can, and often will, reschedule itself, but at any time it will be present on the queue only once).

If there is more than one queue in the code, a Task must not be scheduled in more than one of them.

Note that the reference time is not millis(), but instead the last time at which there was a non-null pop of the queue, or a signal sent to the queue. This is because we assume that the majority of the calls to this method are happening within a run() method, in which case the relevant time is the time when the task was due, not the possibly slightly later actual time. If you need to schedule with respect to the actual current time, or wish to push a task outside a run() method, then you will need to use push_at(millis()+t).

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

Parameters
Tthe task to be queued
intervalthe time after the reference time, in non-negative ms, at which the task is to be queued

◆ push_at()

void TaskQueue::push_at ( Task T,
ms_t  t 
)

Push the given task onto the queue, due at time t.

User code should generally use push_after or Task::run_after, in preference to this function, since that function does the time calculations correctly with respect to the queue reference time, and near the roll-over of the time value. Calling run_at(millis() + 100), say, will work most of the time, but may cause the timing of looping tasks to drift, or sequence things wrongly near roll-over time.

Do not use push_at(Task*, 0) as a way of scheduling a task for ‘immediately’, on the grounds that time 0 is infinitely far in the past and the task is thus immediately overdue. That is not guaranteed to work for all values of the reference time (when the time is more than half way to rolling over, time 0 is in the future). If you wish this effect, instead use push_after with a zero offset, or (in slightly different circumstances) push_immediately.

We can be deemed to take ownership of the new object.

It may not need saying, but scheduling tasks within an interrupt handler is probably an extremely bad idea.

Parameters
Tthe task to be queued, which must be non-null
tthe due time
See also
Task::run_at

◆ push_immediately()

void TaskQueue::push_immediately ( Task T)

Push the given task on the queue, due immediately (in particular, at the current reference time).

This is broadly equivalent to push_after(Task*, ms_t), with an interval argument of zero, except that the task is forced to the head of the queue, even if the current head task is overdue. Thus this task will be due to run the next time that run_ready is called; this is useful if some action is deemed particularly urgent.

If, as is more usually the case, you wish the task to run immediately without this preemption, then use push_after with a zero interval argument.

Parameters
Tthe task to be pushed

◆ remove()

void TaskQueue::remove ( Task T)

Remove from the queue an element which is == to the given Task.

It is OK to 'remove' a task which is not in the queue: in this case, nothing happens.

Parameters
Tthe task to be removed
See also
Task::cancel

◆ remove_all_tasks()

void TaskQueue::remove_all_tasks ( void  )

Discard all of the tasks from the queue, without running them.

We don't do deallocation of any tasks, and we don't call Task::cancel on them. Any tasks that were on the queue may end up in an inconsistent internal state. Thus this method is really only useful for the case where we wish to break out of the main is_empty loop, such as in an error handler (using longjmp, perhaps).

After this operation, is_empty returns true. If you wish to start using the queue again, then you must call initialise on the queue.

◆ run_ready()

bool TaskQueue::run_ready ( ms_t  t)

If the head task in the queue is ready at the given time (ie, its due time is at or before that time), then run it.

The argument passed to this function is the PQRST library's only notion of ‘the current time’.

Parameters
tthe current time
Returns
true if a task was ready and was run

◆ signal()

void TaskQueue::signal ( int  signal,
ms_t  t 
)

Pass the given integer on to each task in the queue.

This calls Task::signal for each such task. There is no significance to the number, as far as this class is concerned. This updates the queue's ‘reference time’ to the time passed in the argument.

It is unspecified what order the tasks in the queue are signalled, but if the signalled tasks add tasks to the queue, those tasks will not receive the signal.

Parameters
signalthe integer to be passed on.
tthe current reference time

◆ size()

unsigned int TaskQueue::size ( bool  inc_maintenance = false) const

Return the number of user tasks currently in the queue.

It is better to call is_empty to test whether the queue is empty, than test whether size() == 0.

Parameters
inc_maintenanceinclude maintenance tasks in the count
Returns
the queue length

◆ traverse_queue()

void TaskQueue::traverse_queue ( traverse_queue_cb_t  callback,
int  max = -1 
)

Traverse the queue.

Run through the queue, calling the provided function once for each entry. This is primarily for debugging; there is no way to change the queue during this traversal.

The callback prototype is either typedef void (*traverse_queue_cb_t)(ms_t, const char* ident, Task*) or typedef void (*traverse_queue_cb_t)(ms_t, Task*) depending on whether PQRST_TASK_IDENT is defined (see the pqrst.h code for further details).

Parameters
callbackthe callback function
maxthe maximum number of tasks to traverse (default: unlimited)

Friends And Related Function Documentation

◆ Task::before

bool Task::before ( const Task other) const
friend

◆ Task::ready_after

ms_t Task::ready_after ( void  ) const
friend

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