Multitasking
Circle provides an optional cooperative non-preemptive scheduler, which allows to solve programming problems based on the process concept. Because in Circle there is only one flat address space with a one-to-one physical-to-virtual address mapping a process in Circle is similar to a thread. In Circle the name “task” is used instead.
Because the scheduler is optional, a Circle application can work without it. The scheduler was introduced to implement TCP/IP networking support, which required many threads of execution at the same time to be implemented even on the one-core Raspberry Pi models. Later porting the VCHIQ driver and HDMI sound support, the accelerated graphics support (Raspberry Pi 1-3 and Zero only) and Wireless LAN support had similar requirements. It can be useful to use the scheduler also for modeling complex user problems in Circle.
The scheduler library provides the following classes:
Class |
Function |
---|---|
CScheduler |
Cooperative non-preemptive scheduler |
CTask |
Representation of a thread of execution, a task |
CMutex |
Mutual exclusion (critical sections) across tasks |
CSemaphore |
Implements the well-known semaphore concept |
CSynchronizationEvent |
Synchronizes the execution of task(s) with an event |
Important
In a multi-core environment (see Multi-core support) all tasks and the scheduler run on CPU core 0.
CScheduler
This class implements a cooperative non-preemptive scheduler, which controls which task runs at a time. Because the scheduler is non-preemptive, a running task has to explicitly release the CPU by sleeping, waiting for a synchronization object (mutex, semaphore, synchronization event) or by calling CScheduler::Get()->Yield()
after a short time, so that the other tasks are able to run. This relatively simple scheduler implements the round-robin policy without task priorities (and without much overhead).
#include <circle/sched/scheduler.h>
-
class CScheduler
-
static boolean CScheduler::IsActive(void)
Returns
TRUE
if the scheduler is available in the system. The scheduler is optional in Circle.
-
static CScheduler *CScheduler::Get(void)
Returns a pointer to the only scheduler object in the system. It must not be called, if the scheduler is not available.
-
CTask *CScheduler::GetCurrentTask(void)
Returns a pointer to the
CTask
object of the currently running task.
-
CTask *CScheduler::GetTask(const char *pTaskName)
Returns a pointer to the
CTask
object of the task with the namepTaskName
or 0, if the task was not found.
-
boolean CScheduler::IsValidTask(CTask *pTask)
Returns
TRUE
, ifpTask
is referencing a CTask object of a currently known task.
-
void CScheduler::Yield(void)
Switch to the next task. The currently running task releases the CPU and the next task in round-robin order, which is not blocked, gets control.
Important
A task should call this from time to time, if it does longer calculations.
-
void CScheduler::Sleep(unsigned nSeconds)
The current task pauses execution for
nSeconds
seconds. The next ready task gets control.
-
void CScheduler::MsSleep(unsigned nMilliSeconds)
The current task pauses execution for
nMilliSeconds
milliseconds. The next ready task gets control.
-
void CScheduler::usSleep(unsigned nMicroSeconds)
The current task pauses execution for
nMicroSeconds
microseconds. The next ready task gets control.
-
void CScheduler::SuspendNewTasks(void)
Causes all new tasks to be created in a suspended state. You can achieve the same, if you set the parameter
bCreateSuspended
toTRUE
, when callingnew
for a task. Nested calls toSuspendNewTasks()
andResumeNewTasks()
are allowed.
-
void CScheduler::ResumeNewTasks(void)
Stops causing new tasks to be created in a suspended state and starts any tasks that were created suspended. Nested calls to
SuspendNewTasks()
andResumeNewTasks()
are allowed.
-
void CScheduler::ListTasks(CDevice *pTarget)
Writes a task listing to the device
pTarget
.
-
void CScheduler::RegisterTaskSwitchHandler(TSchedulerTaskHandler *pHandler)
pHandler
is called on each task switch. This method is normally used by the Linux kernel driver and Pthreads emulation. The handler is called with a pointer to theCTask
object of the task, which gets control now. The prototype of the handler is:
void TSchedulerTaskHandler (CTask *pTask);
-
void CScheduler::RegisterTaskTerminationHandler(TSchedulerTaskHandler *pHandler)
pHandler
is called, when a task terminates. This method is normally used by the Linux kernel driver and Pthreads emulation. The handler is called with a pointer to theCTask
object of the task, which terminates. SeeRegisterTaskSwitchHandler()
for the prototype of the handler.
CTask
Derive this class, define the Run()
method to implement your own task and call new
on it to start it.
#include <circle/sched/task.h>
-
class CTask
-
CTask::CTask(unsigned nStackSize = TASK_STACK_SIZE, boolean bCreateSuspended = FALSE)
Creates a task.
nStackSize
is the stack size for this task. By default a new task is immediately ready to run and itsRun()
method can be called. If you have to do more initialization, before the task can run, setbCreateSuspended
toTRUE
. The task has to be started explicitly by callingStart()
on it then.
-
virtual void CTask::Run(void)
Override this method to define the entry point for your own task. The task is automatically terminated, when
Run()
returns.
-
void CTask::Start(void)
Starts a task, that was created with
bCreateSuspended = TRUE
or restarts it afterSuspend()
.
-
boolean CTask::IsSuspended(void) const
Returns
TRUE
, if the task is currently suspended from running.
-
void CTask::Terminate(void)
Terminates the execution of the task. This method can only be called by the task itself. The task terminates on return from
Run()
too.
-
void CTask::WaitForTermination(void)
Waits for the termination of the task. This method can only be called by an other task.
-
const char *CTask::GetName(void) const
Returns a pointer to 0-terminated name string of this task. The default name of a task is constructed from the address of its task object (e.g.
"@84abc0"
). The main application task has the name"main"
.
CMutex
Provides a method to provide mutual exclusion (critical sections) across tasks.
#include <circle/sched/mutex.h>
-
class CMutex
CSemaphore
Implements the well-known semaphore synchronization concept, which was initially defined by Dijkstra. The class maintains a non-negative counter, which is decremented with the Down()
operation. When this is not possible, because the counter is already zero, the calling task waits, until the counter is incremented again. This is possible with the Up()
operation. Semaphores can be used to control the access to a limited number of resources.
#include <circle/sched/semaphore.h>
-
class CSemaphore
-
CSemaphore::CSemaphore(unsigned nInitialCount = 1)
Creates a semaphore.
nInitialCount
is the initial count of the semaphore.
-
unsigned CSemaphore::GetState(void) const
Returns the current count of the semaphore.
-
void CSemaphore::Down(void)
Decrements the semaphore count. Blocks the calling task, if the count is already zero.
-
void CSemaphore::Up(void)
Increments the semaphore count. Wakes another waiting task, if the count was zero. Can be called from interrupt context.
-
boolean CSemaphore::TryDown(void)
Tries to decrement the semaphore count. Returns
TRUE
on success orFALSE
, if the count is zero.
CSynchronizationEvent
Provides a method to synchronize the execution of tasks with an event. The event can be set or cleared. If a task is waiting for the event, it is blocked, when the event is cleared (unset) and will continue execution, when the event is set again. Multiple tasks can wait for the event at the same time.
#include <circle/sched/synchronizationevent.h>
-
class CSynchronizationEvent
-
CSynchronizationEvent::CSynchronizationEvent(boolean bState = FALSE)
Creates the synchronization event.
bState
is the initial state of the event (default cleared).
-
boolean CSynchronizationEvent::GetState(void)
Returns the current state for the synchronization event.
-
void CSynchronizationEvent::Clear(void)
Clears the synchronization event.
-
void CSynchronizationEvent::Set(void)
Sets the synchronization event. Wakes all tasks currently waiting for the event. Can be called from interrupt context.
-
void CSynchronizationEvent::Wait(void)
Blocks the calling task, if the synchronization event is cleared. The task will wake up, when the event is set later. Multiple tasks can wait for the event to be set.
-
boolean CSynchronizationEvent::WaitWithTimeout(unsigned nMicroSeconds)
Blocks the calling task for
nMicroSeconds
microseconds, if the synchronization event is cleared. The task will wake up, when the event is set later. Multiple tasks can wait for the event to be set. This method returnsTRUE
, ifnMicroSeconds
microseconds have elapsed, before the event has been set. To determine, what caused the method to return, useGetState()
to see, if the event has been set. It is possible to have timed out and the event is set anyway.