LCOV - code coverage report
Current view: top level - asyncFreeRtos/include/async - TaskContext.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 99 102 97.1 %
Date: 2025-08-28 06:39:11 Functions: 21 33 63.6 %

          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          14 : 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         181 : , _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             : }
     197             : 
     198             : template<class Binding>
     199           1 : void TaskContext<Binding>::initTaskHandle(TaskHandle_t const taskHandle)
     200             : {
     201           1 :     _taskHandle = taskHandle;
     202             : }
     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          16 :                         : TaskFunctionType::template create<&TaskContext::defaultTaskFunction>();
     218          13 :     _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           4 :     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           3 : }
     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             :         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          19 :     _taskFunction(*this);
     339             : }
     340             : 
     341             : template<class Binding>
     342           8 : void TaskContext<Binding>::dispatch()
     343             : {
     344           8 :     EventMaskType eventMask = 0U;
     345           8 :     while ((eventMask & STOP_EVENT_MASK) == 0U)
     346             :     {
     347          25 :         eventMask = waitEvents();
     348          58 :         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             : }
     357             : 
     358             : template<class Binding>
     359           1 : void TaskContext<Binding>::dispatchWhileWork()
     360             : {
     361             :     while (true)
     362             :     {
     363           2 :         handleTimeout();
     364           2 :         EventMaskType const eventMask = peekEvents();
     365           2 :         if (eventMask != 0U)
     366             :         {
     367           3 :             handleEvents(eventMask);
     368             :         }
     369             :         else
     370             :         {
     371             :             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             : }
     388             : 
     389             : template<class Binding>
     390           0 : void TaskContext<Binding>::defaultIdleFunction(TaskContext<Binding>& taskContext)
     391             : {
     392           0 :     taskContext.dispatchWhileWork();
     393             : }
     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 1.14