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.4 % 110 106
Test Date: 2026-02-24 11:21:15 Functions: 69.0 % 58 40

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

Generated by: LCOV version 2.0-1