LCOV - code coverage report
Current view: top level - libs/bsw/asyncFreeRtos/include/async - TaskContext.h (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.7 % 120 116
Test Date: 2026-09-11 12:05:06 Functions: 70.0 % 60 42

            Line data    Source code
       1              : /********************************************************************************
       2              :  * Copyright (c) 2024 Accenture
       3              :  *
       4              :  * This program and the accompanying materials are made available under the
       5              :  * terms of the Apache License Version 2.0 which is available at
       6              :  * https://www.apache.org/licenses/LICENSE-2.0
       7              :  *
       8              :  * SPDX-License-Identifier: Apache-2.0
       9              :  ********************************************************************************/
      10              : 
      11              : /**
      12              :  * \ingroup async
      13              :  */
      14              : #pragma once
      15              : 
      16              : #include "async/EventDispatcher.h"
      17              : #include "async/EventPolicy.h"
      18              : #include "async/RunnableExecutor.h"
      19              : #include "async/Types.h"
      20              : 
      21              : #include <bsp/timer/SystemTimer.h>
      22              : #include <etl/delegate.h>
      23              : #include <etl/span.h>
      24              : #include <timer/Timer.h>
      25              : 
      26              : #include <FreeRTOS.h>
      27              : #include <task.h>
      28              : 
      29              : namespace async
      30              : {
      31              : /**
      32              :  * Provides an interface between application-specific Tasks and Timers
      33              :  * and the FreeRTOS framework, managing FreeRTOS* task and timer callbacks.
      34              :  *
      35              :  * The TaskContext class facilitates seamless integration between the application's
      36              :  * task and timer logic and FreeRTOS by invoking necessary FreeRTOS functions
      37              :  * for task creation, scheduling and processing callbacks.
      38              :  *
      39              :  * \tparam Binding The specific binding type associated with the TaskContext.
      40              :  */
      41              : template<class Binding>
      42              : class TaskContext : public EventDispatcher<2U, LockType>
      43              : {
      44              : public:
      45              :     using TaskFunctionType = ::etl::delegate<void(TaskContext<Binding>&)>;
      46              :     using StackType        = ::etl::span<StackType_t>;
      47              : 
      48              :     TaskContext();
      49              : 
      50              :     /**
      51              :      * Initializes a task with a specified context and task function.
      52              :      * \param context The context associated with this task.
      53              :      * \param name The name of this task.
      54              :      * \param taskFunction The function to execute in this task.
      55              :      */
      56              :     void initTask(ContextType context, char const* const name, TaskFunctionType taskFunction);
      57              : 
      58              :     /**
      59              :      * Sets the FreeRTOS task handle for this context.
      60              :      * \param taskHandle The handle to associate with the FreeRTOS task.
      61              :      */
      62              :     void initTaskHandle(TaskHandle_t taskHandle);
      63              : 
      64              :     /**
      65              :      * Creates a FreeRTOS task with specific configuration.
      66              :      * \param context The task's context.
      67              :      * \param task The static task storage structure.
      68              :      * \param name The name of the task.
      69              :      * \param priority The priority of the task.
      70              :      * \param stack The stack allocated for the task.
      71              :      * \param taskFunction The function that the task will execute.
      72              :      */
      73              :     void createTask(
      74              :         ContextType context,
      75              :         StaticTask_t& task,
      76              :         char const* name,
      77              :         UBaseType_t priority,
      78              :         StackType const& stack,
      79              :         TaskFunctionType taskFunction);
      80              : 
      81              :     char const* getName() const;
      82              : 
      83              :     TaskHandle_t getTaskHandle() const;
      84              : 
      85              :     uint32_t getUnusedStackSize() const;
      86              : 
      87              :     /**
      88              :      * Executes asynchronously the specified runnable within this task context.
      89              :      * \param runnable The runnable to execute.
      90              :      */
      91              :     void execute(RunnableType& runnable);
      92              : 
      93              :     /**
      94              :      * Schedules a runnable to execute after a delay.
      95              :      * \param runnable The runnable to schedule.
      96              :      * \param timeout The timeout associated with the runnable.
      97              :      * \param delay The delay before execution.
      98              :      * \param unit The time unit for the delay.
      99              :      */
     100              :     void schedule(RunnableType& runnable, TimeoutType& timeout, uint32_t delay, TimeUnitType unit);
     101              : 
     102              :     /**
     103              :      * Schedules a runnable to execute at a fixed rate.
     104              :      * \param runnable The runnable to schedule.
     105              :      * \param timeout The timeout associated with the runnable.
     106              :      * \param period The period between executions.
     107              :      * \param unit The time unit for the period.
     108              :      */
     109              :     void scheduleAtFixedRate(
     110              :         RunnableType& runnable, TimeoutType& timeout, uint32_t period, TimeUnitType unit);
     111              : 
     112              :     /**
     113              :      * Cancels a scheduled runnable.
     114              :      * \param timeout The timeout associated with the runnable to cancel.
     115              :      */
     116              :     void cancel(TimeoutType& timeout);
     117              : 
     118              :     /**
     119              :      * Suspends the task such that it is no longer scheduled by FreeRTOS.
     120              :      */
     121              :     void suspend();
     122              : 
     123              :     /**
     124              :      * Resumes the task such that it is scheduled by FreeRTOS.
     125              :      */
     126              :     void resume();
     127              : 
     128              :     /// Calls the task's assigned function.
     129              :     void callTaskFunction();
     130              : 
     131              :     /// Dispatches events for processing within this context.
     132              :     void dispatch();
     133              : 
     134              :     /// Stops event dispatching.
     135              :     void stopDispatch();
     136              : 
     137              :     /// Dispatches events while runnable is executing.
     138              :     void dispatchWhileWork();
     139              : 
     140              :     /**
     141              :      * Retrieves the amount of unused stack space for a specified task.
     142              :      * \param taskHandle The handle of the task.
     143              :      * \return The amount of unused stack space.
     144              :      */
     145              :     static uint32_t getUnusedStackSize(TaskHandle_t taskHandle);
     146              : 
     147              :     /**
     148              :      * Default function to be executed by a task within this context.
     149              :      * \param taskContext The context in which the task executes.
     150              :      */
     151              :     static void defaultTaskFunction(TaskContext<Binding>& taskContext);
     152              : 
     153              :     /**
     154              :      * Default function to be executed by the idle task.
     155              :      * \param taskContext The context in which the task executes.
     156              :      */
     157              :     static void defaultIdleFunction(TaskContext<Binding>& taskContext);
     158              : 
     159              : private:
     160              :     friend class EventPolicy<TaskContext<Binding>, 0U>;
     161              :     friend class EventPolicy<TaskContext<Binding>, 1U>;
     162              : 
     163              :     using ExecuteEventPolicyType = EventPolicy<TaskContext<Binding>, 0U>;
     164              :     using TimerEventPolicyType   = EventPolicy<TaskContext<Binding>, 1U>;
     165              :     using TimerType              = ::timer::Timer<LockType>;
     166              : 
     167              :     static EventMaskType const STOP_EVENT_MASK = static_cast<EventMaskType>(
     168              :         static_cast<EventMaskType>(1U) << static_cast<EventMaskType>(EVENT_COUNT));
     169              :     static EventMaskType const WAIT_EVENT_MASK = (STOP_EVENT_MASK << 1U) - 1U;
     170              : 
     171              :     void setEvents(EventMaskType eventMask);
     172              :     EventMaskType waitEvents();
     173              :     EventMaskType peekEvents();
     174              : 
     175              :     void handleTimeout();
     176              : 
     177              :     static void staticTaskFunction(void* param);
     178              : 
     179              :     RunnableExecutor<RunnableType, ExecuteEventPolicyType, LockType> _runnableExecutor;
     180              :     TimerType _timer;
     181              :     TimerEventPolicyType _timerEventPolicy;
     182              :     TaskFunctionType _taskFunction;
     183              :     TaskHandle_t _taskHandle;
     184              :     char const* _name;
     185              :     ContextType _context;
     186              : };
     187              : 
     188              : /**
     189              :  * Inline implementations.
     190              :  */
     191              : template<class Binding>
     192          187 : inline TaskContext<Binding>::TaskContext()
     193          187 : : _runnableExecutor(*this)
     194          187 : , _timerEventPolicy(*this)
     195          187 : , _taskFunction()
     196          187 : , _taskHandle(nullptr)
     197          187 : , _name(nullptr)
     198          374 : , _context(CONTEXT_INVALID)
     199              : {
     200          187 :     _timerEventPolicy.setEventHandler(
     201              :         HandlerFunctionType::create<TaskContext, &TaskContext::handleTimeout>(*this));
     202          187 :     _runnableExecutor.init();
     203          187 : }
     204              : 
     205              : template<class Binding>
     206            1 : void TaskContext<Binding>::initTask(
     207              :     ContextType const context, char const* const name, TaskFunctionType const taskFunction)
     208              : {
     209            1 :     _context      = context;
     210            1 :     _name         = name;
     211            1 :     _taskFunction = taskFunction.is_valid()
     212            1 :                         ? taskFunction
     213            0 :                         : TaskFunctionType::template create<&TaskContext::defaultIdleFunction>();
     214            1 : }
     215              : 
     216              : template<class Binding>
     217            1 : void TaskContext<Binding>::initTaskHandle(TaskHandle_t const taskHandle)
     218              : {
     219            1 :     _taskHandle = taskHandle;
     220            1 : }
     221              : 
     222              : template<class Binding>
     223           14 : void TaskContext<Binding>::createTask(
     224              :     ContextType const context,
     225              :     StaticTask_t& task,
     226              :     char const* const name,
     227              :     UBaseType_t const priority,
     228              :     StackType const& stack,
     229              :     TaskFunctionType const taskFunction)
     230              : {
     231           14 :     _context      = context;
     232           14 :     _name         = name;
     233           14 :     _taskFunction = taskFunction.is_valid()
     234           14 :                         ? taskFunction
     235           15 :                         : TaskFunctionType::template create<&TaskContext::defaultTaskFunction>();
     236           28 :     _taskHandle   = xTaskCreateStatic(
     237              :         &staticTaskFunction,
     238              :         name,
     239           14 :         static_cast<uint32_t>(stack.size()),
     240              :         this,
     241              :         priority,
     242              :         stack.data(),
     243              :         &task);
     244           14 : }
     245              : 
     246              : template<class Binding>
     247            3 : inline char const* TaskContext<Binding>::getName() const
     248              : {
     249            3 :     return _name;
     250              : }
     251              : 
     252              : template<class Binding>
     253            1 : inline TaskHandle_t TaskContext<Binding>::getTaskHandle() const
     254              : {
     255            1 :     return _taskHandle;
     256              : }
     257              : 
     258              : template<class Binding>
     259            2 : inline uint32_t TaskContext<Binding>::getUnusedStackSize() const
     260              : {
     261            2 :     return getUnusedStackSize(_taskHandle);
     262              : }
     263              : 
     264              : template<class Binding>
     265            5 : inline void TaskContext<Binding>::execute(RunnableType& runnable)
     266              : {
     267            5 :     _runnableExecutor.enqueue(runnable);
     268            5 : }
     269              : 
     270              : template<class Binding>
     271            6 : inline void TaskContext<Binding>::schedule(
     272              :     RunnableType& runnable, TimeoutType& timeout, uint32_t const delay, TimeUnitType const unit)
     273              : {
     274            6 :     if (!_timer.isActive(timeout))
     275              :     {
     276            5 :         timeout._runnable = &runnable;
     277            5 :         timeout._context  = _context;
     278            5 :         if (_timer.set(timeout, delay * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
     279              :         {
     280            4 :             _timerEventPolicy.setEvent();
     281              :         }
     282              :     }
     283            6 : }
     284              : 
     285              : template<class Binding>
     286            7 : inline void TaskContext<Binding>::scheduleAtFixedRate(
     287              :     RunnableType& runnable, TimeoutType& timeout, uint32_t const period, TimeUnitType const unit)
     288              : {
     289            7 :     if (!_timer.isActive(timeout))
     290              :     {
     291            6 :         timeout._runnable = &runnable;
     292            6 :         timeout._context  = _context;
     293            6 :         if (_timer.setCyclic(timeout, period * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
     294              :         {
     295            5 :             _timerEventPolicy.setEvent();
     296              :         }
     297              :     }
     298            7 : }
     299              : 
     300              : template<class Binding>
     301            6 : inline void TaskContext<Binding>::cancel(TimeoutType& timeout)
     302              : {
     303            6 :     _timer.cancel(timeout);
     304            6 : }
     305              : 
     306              : template<class Binding>
     307            1 : inline void TaskContext<Binding>::suspend()
     308              : {
     309            1 :     if (_taskHandle != nullptr)
     310              :     {
     311            1 :         vTaskSuspend(_taskHandle);
     312              :     }
     313            1 : }
     314              : 
     315              : template<class Binding>
     316            1 : inline void TaskContext<Binding>::resume()
     317              : {
     318            1 :     if (_taskHandle != nullptr)
     319              :     {
     320            1 :         vTaskResume(_taskHandle);
     321              :     }
     322            1 : }
     323              : 
     324              : template<class Binding>
     325           22 : inline void TaskContext<Binding>::setEvents(EventMaskType const eventMask)
     326              : {
     327           22 :     BaseType_t* const higherPriorityTaskHasWoken = Binding::getHigherPriorityTaskWoken();
     328           22 :     if (higherPriorityTaskHasWoken != nullptr)
     329              :     {
     330            1 :         xTaskNotifyFromISR(_taskHandle, eventMask, eSetBits, higherPriorityTaskHasWoken);
     331              :     }
     332              :     else
     333              :     {
     334           21 :         xTaskNotify(_taskHandle, eventMask, eSetBits);
     335              :     }
     336           22 : }
     337              : 
     338              : template<class Binding>
     339           25 : inline EventMaskType TaskContext<Binding>::waitEvents()
     340              : {
     341           25 :     EventMaskType eventMask = 0U;
     342           25 :     uint32_t ticks          = Binding::WAIT_EVENTS_TICK_COUNT;
     343              :     uint32_t nextDelta;
     344           25 :     bool const hasDelta = _timer.getNextDelta(getSystemTimeUs32Bit(), nextDelta);
     345           25 :     if (hasDelta)
     346              :     {
     347           14 :         ticks = static_cast<uint32_t>((nextDelta + (Config::TICK_IN_US - 1U)) / Config::TICK_IN_US);
     348              :     }
     349           25 :     if (xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, ticks) != 0)
     350              :     {
     351           14 :         return eventMask;
     352              :     }
     353           11 :     else if (hasDelta)
     354              :     {
     355            6 :         return TimerEventPolicyType::EVENT_MASK;
     356              :     }
     357              :     else
     358              :     {
     359            5 :         return 0U;
     360              :     }
     361              : }
     362              : 
     363              : template<class Binding>
     364            2 : inline EventMaskType TaskContext<Binding>::peekEvents()
     365              : {
     366            2 :     EventMaskType eventMask = 0U;
     367            2 :     (void)xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, 0U);
     368            2 :     return eventMask;
     369              : }
     370              : 
     371              : template<class Binding>
     372           10 : void TaskContext<Binding>::callTaskFunction()
     373              : {
     374           10 :     _taskFunction(*this);
     375           10 : }
     376              : 
     377              : template<class Binding>
     378            8 : void TaskContext<Binding>::dispatch()
     379              : {
     380            8 :     EventMaskType eventMask = 0U;
     381           33 :     while ((eventMask & STOP_EVENT_MASK) == 0U)
     382              :     {
     383           25 :         eventMask = waitEvents();
     384           25 :         handleEvents(eventMask);
     385              :     }
     386            8 : }
     387              : 
     388              : template<class Binding>
     389            8 : inline void TaskContext<Binding>::stopDispatch()
     390              : {
     391            8 :     setEvents(STOP_EVENT_MASK);
     392            8 : }
     393              : 
     394              : template<class Binding>
     395            1 : void TaskContext<Binding>::dispatchWhileWork()
     396              : {
     397            1 :     while (true)
     398              :     {
     399            2 :         handleTimeout();
     400            2 :         EventMaskType const eventMask = peekEvents();
     401            2 :         if (eventMask != 0U)
     402              :         {
     403            1 :             handleEvents(eventMask);
     404              :         }
     405              :         else
     406              :         {
     407            1 :             break;
     408              :         }
     409              :     }
     410            1 : }
     411              : 
     412              : template<class Binding>
     413            4 : uint32_t TaskContext<Binding>::getUnusedStackSize(TaskHandle_t const taskHandle)
     414              : {
     415            4 :     return static_cast<uint32_t>(uxTaskGetStackHighWaterMark(taskHandle))
     416            4 :            * static_cast<uint32_t>(sizeof(StackType_t));
     417              : }
     418              : 
     419              : template<class Binding>
     420            8 : void TaskContext<Binding>::defaultTaskFunction(TaskContext<Binding>& taskContext)
     421              : {
     422            8 :     taskContext.dispatch();
     423            8 : }
     424              : 
     425              : template<class Binding>
     426            0 : void TaskContext<Binding>::defaultIdleFunction(TaskContext<Binding>& taskContext)
     427              : {
     428            0 :     taskContext.dispatchWhileWork();
     429            0 : }
     430              : 
     431              : template<class Binding>
     432           12 : void TaskContext<Binding>::handleTimeout()
     433              : {
     434           16 :     while (_timer.processNextTimeout(getSystemTimeUs32Bit())) {}
     435           12 : }
     436              : 
     437              : template<class Binding>
     438            9 : void TaskContext<Binding>::staticTaskFunction(void* const param)
     439              : {
     440            9 :     TaskContext& taskContext = *reinterpret_cast<TaskContext*>(param);
     441            9 :     taskContext.callTaskFunction();
     442            9 : }
     443              : 
     444              : } // namespace async
        

Generated by: LCOV version 2.0-1