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
TRUEif 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
CTaskobject of the currently running task.
-
CTask *CScheduler::GetTask(const char *pTaskName)
Returns a pointer to the
CTaskobject of the task with the namepTaskNameor 0, if the task was not found.
-
boolean CScheduler::IsValidTask(CTask *pTask)
Returns
TRUE, ifpTaskis 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
nSecondsseconds. The next ready task gets control.
-
void CScheduler::MsSleep(unsigned nMilliSeconds)
The current task pauses execution for
nMilliSecondsmilliseconds. The next ready task gets control.
-
void CScheduler::usSleep(unsigned nMicroSeconds)
The current task pauses execution for
nMicroSecondsmicroseconds. 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
bCreateSuspendedtoTRUE, when callingnewfor 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.
-
boolean CScheduler::EnumerateTasks(boolean (*pCallback)(CTask *pTask, const char *pName, TTaskState State, TTaskFlags Flags, void *pParam), void *pParam)
Enumerates all existing tasks. Invokes
pCallbackfor each task.pParamis a user defined pointer, that will back passed to the callback. ReturnsFALSE, if the enumeration was canceled by the callback returningFALSE.
-
enum TTaskState
TaskStateNew
TaskStateReady
TaskStateBlocked
TaskStateBlockedWithTimeout
TaskStateSleeping
TaskStateTerminated
-
enum TTaskFlags
TaskFlagNone
TaskFlagRunning
TaskFlagSuspended
These flag values can be or’ed together.
-
void CScheduler::ListTasks(CDevice *pTarget)
Writes a task listing to the device
pTarget.
-
void CScheduler::RegisterTaskSwitchHandler(TSchedulerTaskHandler *pHandler)
pHandleris 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 theCTaskobject of the task, which gets control now. The prototype of the handler is:
void TSchedulerTaskHandler (CTask *pTask);
-
void CScheduler::RegisterTaskTerminationHandler(TSchedulerTaskHandler *pHandler)
pHandleris 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 theCTaskobject 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.
nStackSizeis 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, setbCreateSuspendedtoTRUE. 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 = TRUEor 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
-
void CMutex::Acquire(void)
Acquires the mutex. The current task blocks, if another task already acquired the mutex. The mutex can be acquired multiple times by the same task.
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.
nInitialCountis 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.
-
boolean CSemaphore::DownWithTimeout(unsigned nMicroSeconds)
Decrements the semaphore count. Blocks the calling task, if the count is already zero. Returns
TRUEafternMicroSecondsmicroseconds, if the semaphore count is still zero, orFALSEotherwise.
-
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
TRUEon 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.
bStateis the initial state of the event (default cleared).
-
boolean CSynchronizationEvent::GetState(void) const
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
nMicroSecondsmicroseconds, 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, ifnMicroSecondsmicroseconds 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.
CPipe
This class implements an unidirectional interprocess communication channel using a FIFO as a way to communicate between tasks. A pipe can operate in blocking (default) or non-blocking mode. In blocking mode writes and reads to/from the pipe block the calling task, when the pipe is not ready (FIFO full/empty). In non-blocking mode -WouldBlock is returned instead in this condition.
#include <circle/sched/pipe.h>
-
class CPipe
-
CPipe::CPipe(boolean bAutoOpen = TRUE, unsigned nFIFOSize = 0x4000, unsigned nAtomicWrite = 512)
Creates an an instance of the pipe.
bAutoOpenis set toFALSEto not automatically open reader and writer once.nFIFOSizeis the size of the internal FIFO (default 16K).nAtomicWriteis the number of bytes to be send atomically (known as PIPE_BUF).
Important
Create this with the new operator, don’t delete it!
-
class CPipeFile
This class encapsulates the read or write endpoint of a
CPipechannel.
Important
Do not directly instantiate this class, use CPipe instead!
-
void CPipeFile::Open(void)
To be called, before the pipe file is reused, or initially if
bAutoOpenwas not set.
-
int CPipeFile::Read(void *pBuffer, size_t nCount)
Reads from the pipe. The read data will be placed at
pBuffer.nCountis the maximum number of bytes to be read. Returns the number of read bytes or-WouldBlock, when the FIFO is empty and non-blocking is off.
-
int CPipeFile::Write(const void *pBuffer, size_t nCount)
Writes to the pipe. The data will be fetched from
pBuffer.nCountis the number of bytes to be written. Returns the number of written bytes or-WouldBlock, when the FIFO is full and non-blocking off, or-NoReader, when all readers have been closed.
struct TStatus
{
boolean bReadReady; // Ready to read without blocking
boolean bWriteReady; // Ready to write without blocking
boolean bException; // Exception arrived (always FALSE)
};