LCOV - code coverage report
Current view: top level - asyncFreeRtos/include/async - TaskContext.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 99 99 100.0 %
Date: 2025-07-04 08:39:33 Functions: 21 29 72.4 %

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

Generated by: LCOV version 1.14