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

Generated by: LCOV version 1.14