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-01-20 13:53:09 Functions: 21 29 72.4 %

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

Generated by: LCOV version 1.14