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-06-18 08:29:03 Functions: 69.0 % 58 40

            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              :     /// Calls the task's assigned function.
     119              :     void callTaskFunction();
     120              : 
     121              :     /// Dispatches events for processing within this context.
     122              :     void dispatch();
     123              : 
     124              :     /// Stops event dispatching.
     125              :     void stopDispatch();
     126              : 
     127              :     /// Dispatches events while runnable is executing.
     128              :     void dispatchWhileWork();
     129              : 
     130              :     /**
     131              :      * Retrieves the amount of unused stack space for a specified task.
     132              :      * \param taskHandle The handle of the task.
     133              :      * \return The amount of unused stack space.
     134              :      */
     135              :     static uint32_t getUnusedStackSize(TaskHandle_t taskHandle);
     136              : 
     137              :     /**
     138              :      * Default function to be executed by a task within this context.
     139              :      * \param taskContext The context in which the task executes.
     140              :      */
     141              :     static void defaultTaskFunction(TaskContext<Binding>& taskContext);
     142              : 
     143              :     /**
     144              :      * Default function to be executed by the idle task.
     145              :      * \param taskContext The context in which the task executes.
     146              :      */
     147              :     static void defaultIdleFunction(TaskContext<Binding>& taskContext);
     148              : 
     149              : private:
     150              :     friend class EventPolicy<TaskContext<Binding>, 0U>;
     151              :     friend class EventPolicy<TaskContext<Binding>, 1U>;
     152              : 
     153              :     using ExecuteEventPolicyType = EventPolicy<TaskContext<Binding>, 0U>;
     154              :     using TimerEventPolicyType   = EventPolicy<TaskContext<Binding>, 1U>;
     155              :     using TimerType              = ::timer::Timer<LockType>;
     156              : 
     157              :     static EventMaskType const STOP_EVENT_MASK = static_cast<EventMaskType>(
     158              :         static_cast<EventMaskType>(1U) << static_cast<EventMaskType>(EVENT_COUNT));
     159              :     static EventMaskType const WAIT_EVENT_MASK = (STOP_EVENT_MASK << 1U) - 1U;
     160              : 
     161              :     void setEvents(EventMaskType eventMask);
     162              :     EventMaskType waitEvents();
     163              :     EventMaskType peekEvents();
     164              : 
     165              :     void handleTimeout();
     166              : 
     167              :     static void staticTaskFunction(void* param);
     168              : 
     169              :     RunnableExecutor<RunnableType, ExecuteEventPolicyType, LockType> _runnableExecutor;
     170              :     TimerType _timer;
     171              :     TimerEventPolicyType _timerEventPolicy;
     172              :     TaskFunctionType _taskFunction;
     173              :     TaskHandle_t _taskHandle;
     174              :     char const* _name;
     175              :     ContextType _context;
     176              : };
     177              : 
     178              : /**
     179              :  * Inline implementations.
     180              :  */
     181              : template<class Binding>
     182          181 : inline TaskContext<Binding>::TaskContext()
     183          181 : : _runnableExecutor(*this)
     184          181 : , _timerEventPolicy(*this)
     185          181 : , _taskFunction()
     186          181 : , _taskHandle(nullptr)
     187          181 : , _name(nullptr)
     188          362 : , _context(CONTEXT_INVALID)
     189              : {
     190          181 :     _timerEventPolicy.setEventHandler(
     191              :         HandlerFunctionType::create<TaskContext, &TaskContext::handleTimeout>(*this));
     192          181 :     _runnableExecutor.init();
     193          181 : }
     194              : 
     195              : template<class Binding>
     196            1 : void TaskContext<Binding>::initTask(
     197              :     ContextType const context, char const* const name, TaskFunctionType const taskFunction)
     198              : {
     199            1 :     _context      = context;
     200            1 :     _name         = name;
     201            1 :     _taskFunction = taskFunction.is_valid()
     202              :                         ? taskFunction
     203            0 :                         : TaskFunctionType::template create<&TaskContext::defaultIdleFunction>();
     204            1 : }
     205              : 
     206              : template<class Binding>
     207            1 : void TaskContext<Binding>::initTaskHandle(TaskHandle_t const taskHandle)
     208              : {
     209            1 :     _taskHandle = taskHandle;
     210            1 : }
     211              : 
     212              : template<class Binding>
     213           13 : void TaskContext<Binding>::createTask(
     214              :     ContextType const context,
     215              :     StaticTask_t& task,
     216              :     char const* const name,
     217              :     UBaseType_t const priority,
     218              :     StackType const& stack,
     219              :     TaskFunctionType const taskFunction)
     220              : {
     221           13 :     _context      = context;
     222           13 :     _name         = name;
     223           13 :     _taskFunction = taskFunction.is_valid()
     224              :                         ? taskFunction
     225           15 :                         : TaskFunctionType::template create<&TaskContext::defaultTaskFunction>();
     226           26 :     _taskHandle   = xTaskCreateStatic(
     227              :         &staticTaskFunction,
     228              :         name,
     229           13 :         static_cast<uint32_t>(stack.size()),
     230              :         this,
     231              :         priority,
     232              :         stack.data(),
     233              :         &task);
     234           13 : }
     235              : 
     236              : template<class Binding>
     237            3 : inline char const* TaskContext<Binding>::getName() const
     238              : {
     239            3 :     return _name;
     240              : }
     241              : 
     242              : template<class Binding>
     243            1 : inline TaskHandle_t TaskContext<Binding>::getTaskHandle() const
     244              : {
     245            1 :     return _taskHandle;
     246              : }
     247              : 
     248              : template<class Binding>
     249            2 : inline uint32_t TaskContext<Binding>::getUnusedStackSize() const
     250              : {
     251            2 :     return getUnusedStackSize(_taskHandle);
     252              : }
     253              : 
     254              : template<class Binding>
     255            5 : inline void TaskContext<Binding>::execute(RunnableType& runnable)
     256              : {
     257            5 :     _runnableExecutor.enqueue(runnable);
     258            5 : }
     259              : 
     260              : template<class Binding>
     261            6 : inline void TaskContext<Binding>::schedule(
     262              :     RunnableType& runnable, TimeoutType& timeout, uint32_t const delay, TimeUnitType const unit)
     263              : {
     264            6 :     if (!_timer.isActive(timeout))
     265              :     {
     266            5 :         timeout._runnable = &runnable;
     267            5 :         timeout._context  = _context;
     268            5 :         if (_timer.set(timeout, delay * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
     269              :         {
     270            4 :             _timerEventPolicy.setEvent();
     271              :         }
     272              :     }
     273            6 : }
     274              : 
     275              : template<class Binding>
     276            7 : inline void TaskContext<Binding>::scheduleAtFixedRate(
     277              :     RunnableType& runnable, TimeoutType& timeout, uint32_t const period, TimeUnitType const unit)
     278              : {
     279            7 :     if (!_timer.isActive(timeout))
     280              :     {
     281            6 :         timeout._runnable = &runnable;
     282            6 :         timeout._context  = _context;
     283            6 :         if (_timer.setCyclic(timeout, period * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
     284              :         {
     285            5 :             _timerEventPolicy.setEvent();
     286              :         }
     287              :     }
     288            7 : }
     289              : 
     290              : template<class Binding>
     291            6 : inline void TaskContext<Binding>::cancel(TimeoutType& timeout)
     292              : {
     293            6 :     _timer.cancel(timeout);
     294            6 : }
     295              : 
     296              : template<class Binding>
     297           22 : inline void TaskContext<Binding>::setEvents(EventMaskType const eventMask)
     298              : {
     299           22 :     BaseType_t* const higherPriorityTaskHasWoken = Binding::getHigherPriorityTaskWoken();
     300           22 :     if (higherPriorityTaskHasWoken != nullptr)
     301              :     {
     302            1 :         xTaskNotifyFromISR(_taskHandle, eventMask, eSetBits, higherPriorityTaskHasWoken);
     303              :     }
     304              :     else
     305              :     {
     306           21 :         xTaskNotify(_taskHandle, eventMask, eSetBits);
     307              :     }
     308           22 : }
     309              : 
     310              : template<class Binding>
     311           25 : inline EventMaskType TaskContext<Binding>::waitEvents()
     312              : {
     313           25 :     EventMaskType eventMask = 0U;
     314           25 :     uint32_t ticks          = Binding::WAIT_EVENTS_TICK_COUNT;
     315              :     uint32_t nextDelta;
     316           25 :     bool const hasDelta = _timer.getNextDelta(getSystemTimeUs32Bit(), nextDelta);
     317           25 :     if (hasDelta)
     318              :     {
     319           14 :         ticks = static_cast<uint32_t>((nextDelta + (Config::TICK_IN_US - 1U)) / Config::TICK_IN_US);
     320              :     }
     321           25 :     if (xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, ticks) != 0)
     322              :     {
     323           14 :         return eventMask;
     324              :     }
     325           11 :     else if (hasDelta)
     326              :     {
     327            6 :         return TimerEventPolicyType::EVENT_MASK;
     328              :     }
     329              :     else
     330              :     {
     331            5 :         return 0U;
     332              :     }
     333              : }
     334              : 
     335              : template<class Binding>
     336            2 : inline EventMaskType TaskContext<Binding>::peekEvents()
     337              : {
     338            2 :     EventMaskType eventMask = 0U;
     339            2 :     (void)xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, 0U);
     340            2 :     return eventMask;
     341              : }
     342              : 
     343              : template<class Binding>
     344           10 : void TaskContext<Binding>::callTaskFunction()
     345              : {
     346           10 :     _taskFunction(*this);
     347           10 : }
     348              : 
     349              : template<class Binding>
     350            8 : void TaskContext<Binding>::dispatch()
     351              : {
     352            8 :     EventMaskType eventMask = 0U;
     353           33 :     while ((eventMask & STOP_EVENT_MASK) == 0U)
     354              :     {
     355           25 :         eventMask = waitEvents();
     356           25 :         handleEvents(eventMask);
     357              :     }
     358            8 : }
     359              : 
     360              : template<class Binding>
     361            8 : inline void TaskContext<Binding>::stopDispatch()
     362              : {
     363            8 :     setEvents(STOP_EVENT_MASK);
     364            8 : }
     365              : 
     366              : template<class Binding>
     367            2 : void TaskContext<Binding>::dispatchWhileWork()
     368              : {
     369            1 :     while (true)
     370              :     {
     371            2 :         handleTimeout();
     372            2 :         EventMaskType const eventMask = peekEvents();
     373            2 :         if (eventMask != 0U)
     374              :         {
     375            1 :             handleEvents(eventMask);
     376              :         }
     377              :         else
     378              :         {
     379            1 :             break;
     380              :         }
     381              :     }
     382            1 : }
     383              : 
     384              : template<class Binding>
     385            4 : uint32_t TaskContext<Binding>::getUnusedStackSize(TaskHandle_t const taskHandle)
     386              : {
     387            4 :     return static_cast<uint32_t>(uxTaskGetStackHighWaterMark(taskHandle))
     388            4 :            * static_cast<uint32_t>(sizeof(StackType_t));
     389              : }
     390              : 
     391              : template<class Binding>
     392            8 : void TaskContext<Binding>::defaultTaskFunction(TaskContext<Binding>& taskContext)
     393              : {
     394            8 :     taskContext.dispatch();
     395            8 : }
     396              : 
     397              : template<class Binding>
     398            0 : void TaskContext<Binding>::defaultIdleFunction(TaskContext<Binding>& taskContext)
     399              : {
     400            0 :     taskContext.dispatchWhileWork();
     401            0 : }
     402              : 
     403              : template<class Binding>
     404           12 : void TaskContext<Binding>::handleTimeout()
     405              : {
     406           16 :     while (_timer.processNextTimeout(getSystemTimeUs32Bit())) {}
     407           12 : }
     408              : 
     409              : template<class Binding>
     410            9 : void TaskContext<Binding>::staticTaskFunction(void* const param)
     411              : {
     412            9 :     TaskContext& taskContext = *reinterpret_cast<TaskContext*>(param);
     413            9 :     taskContext.callTaskFunction();
     414            9 : }
     415              : 
     416              : } // namespace async
        

Generated by: LCOV version 2.0-1