FreeRTOS Task Scheduler
- FreeRTOS Task Scheduler
- Location
- Type
- Background
- Overview
- Detailed Design
- API
- Configuring the Scheduler Size
- Adding a Task to the Scheduler
- Starting the Scheduler
- Task Execution
- Retrieving a Task
- Removing a Task from the Scheduler
- Caveats
- Future Advancements
- Testing Plan
- Unit Testing Scheme
- Demonstration Project
Location
systems
Type
Implementation
Background
The TaskScheduler
along with the
Task Interface
provide an
abstraction layer for the management of FreeRTOS tasks.
Overview
The TaskScheduler is a FreeRTOS task scheduler responsible for scheduling tasks
that inherit the Task
interface. The scheduler utilizes the FreeRTOS Event
Group to ensure all tasks have been successfully initialized before proceeding
to the execution any task.
Detailed Design
API
class TaskInterface;
class TaskSchedulerInterface
{
public:
virtual EventGroupHandle_t GetPreRunEventGroupHandle() const = 0;
virtual EventBits_t GetPreRunSyncBits() const = 0;
virtual uint8_t GetTaskCount() const = 0;
virtual void AddTask(TaskInterface * task) = 0;
virtual void RemoveTask(const char * task_name) = 0;
virtual TaskInterface * GetTask(const char * task_name) = 0;
virtual uint8_t GetTaskIndex(const char * task_name) const = 0;
virtual TaskInterface * const * GetAllTasks() const = 0;
};
class TaskScheduler : public TaskSchedulerInterface
{
public:
TaskScheduler();
EventGroupHandle_t GetPreRunEventGroupHandle() const override;
EventBits_t GetPreRunSyncBits() const override;
uint8_t GetTaskCount() const override;
void AddTask(TaskInterface * task) override;
void RemoveTask(const char * task_name) override;
TaskInterface * GetTask(const char * task_name) override;
uint8_t GetTaskIndex(const char * task_name) const override;
TaskInterface * const * GetAllTasks() const override;
void Start();
private:
static void RunTask(void * task_pointer);
void InitializeAllTasks();
TaskInterface * task_list_[config::kTaskSchedulerSize];
uint8_t task_count_;
StaticEventGroup_t pre_run_event_group_buffer_;
EventGroupHandle_t pre_run_event_group_handle_;
EventBits_t pre_run_sync_bits_;
};
Configuring the Scheduler Size
By default the TaskScheduler
's size is configured to schedule a maximum of 16
tasks in config.hpp
.
#if !defined SJ2_TASK_SCHEDULER_SIZE
#define SJ2_TASK_SCHEDULER_SIZE 16
#endif // !defined SJ2_TASK_SCHEDULER_SIZE
SJ2_DECLARE_CONSTANT(TASK_SCHEDULER_SIZE, uint32_t, kTaskSchedulerSize);
Note: This configuration can be changed by defining your own
SJ2_TASK_SCHEDULER_SIZE
in theproject_config.hpp
.
Adding a Task to the Scheduler
All tasks inheriting the Task
interface is automatically added to the
scheduler when constructed.
void AddTask(TaskInterface * task) override
Adds a task to the scheduler. If the scheduler is full, an error is asserted to recommend an increase in the default task scheduler size.
Starting the Scheduler
The TaskScheduler
can be started by invoking the Start()
method.
void Start() override
Invoked to start the scheduler. The following sequence is performed:
- Check the task_list_ to ensure that there is at least one task added to the scheduler. If no tasks are added, output a warning message to notify the user that there a currently no scheduled tasks.
- Invoke
InitializeAllTasks()
to initialize all scheduled tasks. - Invoke
vTaskStartScheduler()
to start executing the scheduled tasks.
void InitializeAllTasks() override
The following sequence is performed when initializing scheduled tasks:
- Statically create a FreeRTOS task through the
xTaskCreateStatic()
method for each task scheduled in thetask_list_
. - Check the returned handle from
xTaskCreateStatic()
. If the handle is anullptr
, then the task was created unsuccessfully and an error is asserted. - If the task was created successfully, invoke
Setup()
of each task. If a task fails to completeSetup()
, terminate initialization and assert an error throughSJ2_ASSERT_FATAL
. - If the task was successfully created and successfully completes
Setup()
, set the task’s event sync bit inpre_run_sync_bits_
based on the task’s index position intask_list_
. - Statically create the Event Group handler.
Task Execution
Each task is responsible for completing its own PreRun()
and wait for the
PreRun()
of all other tasks to be completed before continuing to execute
Run()
.
EventGroupHandle_t pre_run_event_group_handle_;
StaticEventGroup_t pre_run_event_group_buffer_;
EventBits_t pre_run_sync_bits_;
PreRun()
of each task. When
a task completes its PreRun()
it broadcasts a notification by setting its sync
bit and waits for all other task sync bits to be set.
More information regarding FreeRTOS Event Groups can be found here.
static void RunTask(void * task_pointer)
The following sequence is performed, where Step 1 is only performed once:
- Get the PreRun Event Group handle and determine the running task’s sync bit by retrieving its task index from the scheduler's task list.
- Invoke the task’s
PreRun()
and assert a fatal error if the task’sPreRun()
fails to complete. If PreRun() completes successfully, set the task’s sync bit in the Event Group and wait for all other task’s sync bits to be set. - When all sync bits are set,
Run()
can now begin. If a desired delay time is set, invokevTaskDelayUntil()
.
Retrieving a Task
TaskInterface * GetTask(const char * task_name) override
Returns a pointer reference of the task by its task name. If the task does not
exist in the scheduler, a nullptr
is returned.
uint8_t GetTaskIndex(const char * task_name) const override
Returns the index of a task in the task_list_
by the provided task_name
.
This method is used by the scheduler the manage a task's PreRun Event Group sync
bit. If the task does not exist in the scheduler, the maximum task count + 1 is
returned. For example, if the maximum task count is 16, then 17 is returned if
the task does not exist.
TaskInterface * const * GetAllTasks() const override
Returns a pointer reference to an immutable array of all currently scheduled tasks.
Removing a Task from the Scheduler
void RemoveTask(const char * task_name) override
The following is performed to remove a specified task by its task_name
:
- Retrieves the task using
GetTask()
and removes the task by invokingvTaskDelete()
. - Sets the reference in
task_list_
tonullptr
. - Decrement the
task_count_
.
Caveats
N/A
Future Advancements
N/A
Testing Plan
Unit Testing Scheme
A total of 17 tasks shall be mocked and stubbed to be used as test inputs for the scheduler. Additionally the following methods from the FreeRTOS library shall be faked:
- vTaskCreateStatic()
- xEventGroupCreateStatic()
- vTaskStartScheduler()
- vTaskDelete()
The following functions of the TaskScheduler
are tested:
AddTask()
- Each mock task shall be added to the scheduler:
- The task count should be incremented for each added task.
- The task count should not exceed
16
.
GetTask()
- Tasks 1-17 shall be added to the scheduler, with each addition:
- The function should return a scheduled task corresponding to the specified task name.
- Task named "Task 17" shall be retrieved.
- The function should return
nullptr
.
GetTaskIndex()
- Tasks 1-17 shall be added to the scheduler, with each addition:
- The function should return the index of the task with the matching task name of the task that is being added.
- The task count should be incremented by 1.
- The index of task named "Does not exist" shall be retrieved.
- The function should return
17
.
- The function should return
RemoveTask()
- The scheduler shall be empty, and a task named "Task A" shall be removed:
vTaskDelete()
should have a call count of0
.- Mock tasks named "Task 1", "Task 2", "Task 3", and "Task 4" shall be added to simulate a scheduler that is not empty:
GetTaskCount()
should return4
.- The mock task at index = 2, "Task 3", shall be removed:
vTaskDelete()
should have a call count of1
.- Total task count should be
3
. - Checking index
2
of the task list should be anullptr
.
Start()
Tasks 1-16 shall be added to the scheduler where:
- Each mock task's
Setup()
should be invoked once. vTaskCreateStatic()
should have a call count of16
.xEventGroupCreateStatic()
should have a call count of1
.GetPreRunEventGroupHandle()
should return the dummy event group handler.GetPreRunSyncBits()
should return0xFFFF
.vTaskStartScheduler()
should have a call count of1
.
Demonstration Project
A demonstration project can be found here.