Line data Source code
1 : // Copyright 2024 Accenture.
2 :
3 : /**
4 : * \ingroup async
5 : */
6 : #ifndef GUARD_DB9C7111_F66E_4D50_B724_5DF992A19ED0
7 : #define GUARD_DB9C7111_F66E_4D50_B724_5DF992A19ED0
8 :
9 : #include "async/TaskContext.h"
10 : #include "async/TaskInitializer.h"
11 :
12 : #ifndef ASYNC_LOOPER_DISABLE
13 : #include <looper/looper.h>
14 : #endif // ASYNC_LOOPER_DISABLE
15 :
16 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
17 : #include <util/timeout/AsyncTimeoutManager.h>
18 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
19 :
20 : #include <estd/array.h>
21 : #include <estd/singleton.h>
22 :
23 : namespace async
24 : {
25 : namespace internal
26 : {
27 : template<bool HasNestedInterrupts = (ASYNC_CONFIG_NESTED_INTERRUPTS != 0)>
28 10 : class NestedInterruptLock : public LockType
29 : {};
30 :
31 : template<>
32 : class NestedInterruptLock<false>
33 : {};
34 :
35 : template<size_t N, typename TaskConfig = ASYNC_TASK_CONFIG_TYPE>
36 : class TaskConfigHolder
37 : {
38 : public:
39 : using TaskConfigType = TaskConfig;
40 :
41 : static void setTaskConfig(size_t taskIdx, TaskConfigType const& taskConfig);
42 :
43 : static TaskConfigType const* getTaskConfig(size_t taskIdx);
44 :
45 : private:
46 : static ::estd::array<TaskConfigType, N> _taskConfigs;
47 : };
48 :
49 : template<size_t N>
50 : class TaskConfigHolder<N, void>
51 : {
52 : public:
53 : struct TaskConfigType
54 : {};
55 :
56 : static void setTaskConfig(size_t taskIdx, TaskConfigType const& taskConfig);
57 :
58 : static TaskConfigType const* getTaskConfig(size_t taskIdx);
59 : };
60 :
61 : } // namespace internal
62 :
63 : /**
64 : * Adapter class bridging FreeRTOS functionalities with the application's binding.
65 : *
66 : * The `FreeRtosAdapter` class serves as a centralized interface for managing tasks, timers,
67 : * and scheduling functionalities within a FreeRTOS environment. It utilizes the specified
68 : * `Binding` type to adapt and configure task-related components, such as `TaskContext`,
69 : * `TaskConfig`, and idle and timer tasks.
70 : *
71 : * \tparam Binding The binding type specifying application-specific configurations.
72 : */
73 : template<class Binding>
74 : class FreeRtosAdapter
75 : {
76 : public:
77 : static size_t const TASK_COUNT = Binding::TASK_COUNT;
78 : static size_t const FREERTOS_TASK_COUNT = TASK_COUNT + 1U;
79 : static TickType_t const WAIT_EVENTS_TICK_COUNT
80 : = static_cast<TickType_t>(Binding::WAIT_EVENTS_TICK_COUNT);
81 : static ContextType const TASK_IDLE = 0U;
82 : static ContextType const TASK_TIMER = static_cast<ContextType>(TASK_COUNT);
83 :
84 : using AdapterType = FreeRtosAdapter<Binding>;
85 :
86 : using TaskContextType = TaskContext<AdapterType>;
87 : using TaskFunctionType = typename TaskContextType::TaskFunctionType;
88 :
89 : using TaskConfigsType = internal::TaskConfigHolder<FREERTOS_TASK_COUNT>;
90 : using TaskConfigType = typename TaskConfigsType::TaskConfigType;
91 :
92 : template<size_t StackSize>
93 : using Stack = internal::Stack<StackSize>;
94 : using TaskInitializer = internal::TaskInitializer<AdapterType>;
95 :
96 : template<size_t StackSize = 0U>
97 : using IdleTask = internal::IdleTask<AdapterType, StackSize>;
98 : template<size_t StackSize = 0U>
99 : using TimerTask = internal::TimerTask<AdapterType, StackSize>;
100 : template<ContextType Context, size_t StackSize = 0U>
101 : using Task = internal::Task<AdapterType, Context, StackSize>;
102 :
103 : /// Struct representing the stack usage for a specific task.
104 : struct StackUsage
105 : {
106 : StackUsage();
107 :
108 : uint32_t _stackSize;
109 : uint32_t _usedSize;
110 : };
111 :
112 : /**
113 : * Retrieves memory pointers for a FreeRTOS task.
114 : *
115 : * \tparam Context The task context.
116 : * \param ppxTaskTCBBuffer Pointer to the task control block buffer.
117 : * \param ppxTaskStackBuffer Pointer to the task stack buffer.
118 : * \param pulTaskStackSize Pointer to the task stack size.
119 : */
120 : template<ContextType Context>
121 : static void getTaskMemory(
122 : StaticTask_t** ppxTaskTCBBuffer,
123 : StackType_t** ppxTaskStackBuffer,
124 : uint32_t* pulTaskStackSize);
125 :
126 : static char const* getTaskName(size_t taskIdx);
127 :
128 : static TaskHandle_t getTaskHandle(size_t taskIdx);
129 :
130 : static TaskConfigType const* getTaskConfig(size_t taskIdx);
131 :
132 : static ContextType getCurrentTaskContext();
133 :
134 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
135 : /**
136 : * Retrieves the timeout manager for a given context.
137 : *
138 : * \param context The task context.
139 : * \return A reference to the timeout manager.
140 : */
141 : static ::common::ITimeoutManager2& getTimeoutManager(ContextType context);
142 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
143 :
144 : #ifndef ASYNC_LOOPER_DISABLE
145 : /**
146 : * Retrieves the looper for a given context.
147 : *
148 : * \param context The task context.
149 : * \return A reference to the looper.
150 : */
151 : static ::loop::looper& getLooper(ContextType context);
152 : #endif // ASYNC_LOOPER_DISABLE
153 :
154 : /// Initializes the FreeRTOS adapter and associated tasks.
155 : static void init();
156 :
157 : /// Starts the FreeRTOS scheduler.
158 : static void run();
159 :
160 : static void runningHook();
161 :
162 : /**
163 : * Retrieves the stack usage for a specified task.
164 : *
165 : * \param taskIdx The index of the task.
166 : * \param stackUsage A reference to the StackUsage struct to populate.
167 : * \return True if stack usage information is successfully retrieved.
168 : */
169 : static bool getStackUsage(size_t taskIdx, StackUsage& stackUsage);
170 :
171 : static void callIdleTaskFunction();
172 :
173 : /**
174 : * Executes a specified runnable within a given context.
175 : *
176 : * \param context The task context.
177 : * \param runnable The runnable to execute.
178 : */
179 : static void execute(ContextType context, RunnableType& runnable);
180 :
181 : /**
182 : * Schedules a runnable to execute after a delay.
183 : *
184 : * \param context The task context.
185 : * \param runnable The runnable to schedule.
186 : * \param timeout The timeout associated with the runnable.
187 : * \param delay The delay before execution.
188 : * \param unit The time unit for the delay.
189 : */
190 : static void schedule(
191 : ContextType context,
192 : RunnableType& runnable,
193 : TimeoutType& timeout,
194 : uint32_t delay,
195 : TimeUnitType unit);
196 :
197 : /**
198 : * Schedules a runnable to execute at a fixed rate.
199 : *
200 : * \param context The task context.
201 : * \param runnable The runnable to schedule.
202 : * \param timeout The timeout associated with the runnable.
203 : * \param delay The delay before the first execution.
204 : * \param unit The time unit for the delay.
205 : */
206 : static void scheduleAtFixedRate(
207 : ContextType context,
208 : RunnableType& runnable,
209 : TimeoutType& timeout,
210 : uint32_t delay,
211 : TimeUnitType unit);
212 :
213 : /**
214 : * Cancels a scheduled runnable.
215 : *
216 : * \param timeout The timeout associated with the runnable to cancel.
217 : */
218 : static void cancel(TimeoutType& timeout);
219 :
220 : /// Notifies the system of an interrupt entry.
221 : static void enterIsr();
222 :
223 : /// Notifies the system of an interrupt exit.
224 : static void leaveIsr();
225 :
226 : /**
227 : * Exits the ISR without yielding control.
228 : * \return True if yielding is not required after ISR.
229 : */
230 : static bool leaveIsrNoYield();
231 :
232 : /// \return A pointer to the task woken by the ISR.
233 : static BaseType_t* getHigherPriorityTaskWoken();
234 :
235 : private:
236 : friend struct internal::TaskInitializer<AdapterType>;
237 :
238 : static void initTask(TaskInitializer& initializer);
239 :
240 : static void initTask(ContextType context);
241 :
242 : static void TaskFunction(void* param);
243 :
244 : static TaskInitializer* _idleTaskInitializer;
245 : static TaskInitializer* _timerTaskInitializer;
246 : static ::estd::array<TaskContextType, TASK_COUNT> _taskContexts;
247 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
248 : static ::estd::array<::common::AsyncTimeoutManager, TASK_COUNT> _timeoutManagers;
249 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
250 : #ifndef ASYNC_LOOPER_DISABLE
251 : static ::estd::array<::loop::looper, TASK_COUNT> _loopers;
252 : #endif // ASYNC_LOOPER_DISABLE
253 : static ::estd::array<uint32_t, FREERTOS_TASK_COUNT> _stackSizes;
254 : static char const* _timerTaskName;
255 : static BaseType_t _higherPriorityTaskWokenFlag;
256 : static BaseType_t* _higherPriorityTaskWoken;
257 : static uint8_t _nestedInterruptCount;
258 : };
259 :
260 : /**
261 : * Inline implementations.
262 : */
263 : template<class Binding>
264 : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_idleTaskInitializer
265 : = nullptr;
266 : template<class Binding>
267 : typename FreeRtosAdapter<Binding>::TaskInitializer* FreeRtosAdapter<Binding>::_timerTaskInitializer
268 : = nullptr;
269 : template<class Binding>
270 : ::estd::
271 : array<typename FreeRtosAdapter<Binding>::TaskContextType, FreeRtosAdapter<Binding>::TASK_COUNT>
272 : FreeRtosAdapter<Binding>::_taskContexts;
273 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
274 : template<class Binding>
275 : ::estd::array<::common::AsyncTimeoutManager, FreeRtosAdapter<Binding>::TASK_COUNT>
276 : FreeRtosAdapter<Binding>::_timeoutManagers;
277 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
278 : #ifndef ASYNC_LOOPER_DISABLE
279 : template<class Binding>
280 : ::estd::array<::loop::looper, FreeRtosAdapter<Binding>::TASK_COUNT>
281 : FreeRtosAdapter<Binding>::_loopers;
282 : #endif // ASYNC_LOOPER_DISABLE
283 : template<class Binding>
284 : ::estd::array<uint32_t, FreeRtosAdapter<Binding>::FREERTOS_TASK_COUNT>
285 : FreeRtosAdapter<Binding>::_stackSizes;
286 : template<class Binding>
287 : char const* FreeRtosAdapter<Binding>::_timerTaskName;
288 : template<class Binding>
289 : BaseType_t FreeRtosAdapter<Binding>::_higherPriorityTaskWokenFlag = 0;
290 : template<class Binding>
291 : BaseType_t* FreeRtosAdapter<Binding>::_higherPriorityTaskWoken = nullptr;
292 : template<class Binding>
293 : uint8_t FreeRtosAdapter<Binding>::_nestedInterruptCount = 0U;
294 :
295 : template<class Binding>
296 : template<ContextType Context>
297 2 : void FreeRtosAdapter<Binding>::getTaskMemory(
298 : StaticTask_t** const ppxTaskTCBBuffer,
299 : StackType_t** const ppxTaskStackBuffer,
300 : uint32_t* const pulTaskStackSize)
301 : {
302 2 : TaskInitializer& initializer
303 : = *((Context == TASK_IDLE) ? _idleTaskInitializer : _timerTaskInitializer);
304 2 : estd_assert(ppxTaskTCBBuffer != nullptr);
305 2 : estd_assert(ppxTaskStackBuffer != nullptr);
306 2 : estd_assert(pulTaskStackSize != nullptr);
307 2 : *ppxTaskTCBBuffer = &initializer._task;
308 2 : *ppxTaskStackBuffer = initializer._stack.data();
309 : // Conversion to uint32_t is OK, stack will not exceed 4GB.
310 2 : *pulTaskStackSize = static_cast<uint32_t>(initializer._stack.size());
311 2 : }
312 :
313 : template<class Binding>
314 3 : inline char const* FreeRtosAdapter<Binding>::getTaskName(size_t const taskIdx)
315 : {
316 4 : return (taskIdx < _taskContexts.size()) ? _taskContexts[taskIdx].getName() : _timerTaskName;
317 : }
318 :
319 : template<class Binding>
320 1 : inline TaskHandle_t FreeRtosAdapter<Binding>::getTaskHandle(size_t const taskIdx)
321 : {
322 1 : return _taskContexts[taskIdx].getTaskHandle();
323 : }
324 :
325 : template<class Binding>
326 : inline typename FreeRtosAdapter<Binding>::TaskConfigType const*
327 1 : FreeRtosAdapter<Binding>::getTaskConfig(size_t const taskIdx)
328 : {
329 1 : return TaskConfigsType::getTaskConfig(taskIdx);
330 : }
331 :
332 : template<class Binding>
333 7 : ContextType FreeRtosAdapter<Binding>::getCurrentTaskContext()
334 : {
335 7 : if (_higherPriorityTaskWoken == nullptr)
336 : {
337 6 : return static_cast<ContextType>(uxTaskGetTaskNumber(xTaskGetCurrentTaskHandle()));
338 : }
339 : return CONTEXT_INVALID;
340 : }
341 :
342 : template<class Binding>
343 8 : void FreeRtosAdapter<Binding>::initTask(TaskInitializer& initializer)
344 : {
345 8 : ContextType const context = initializer._context;
346 8 : _stackSizes[static_cast<size_t>(context)] = static_cast<uint32_t>(
347 8 : static_cast<size_t>(initializer._stack.size()) * sizeof(BaseType_t));
348 : TaskConfigsType::setTaskConfig(context, initializer._config);
349 8 : if (context == TASK_TIMER)
350 : {
351 2 : _timerTaskInitializer = &initializer;
352 2 : _timerTaskName = initializer._name;
353 : }
354 : else
355 : {
356 6 : initTask(context);
357 6 : TaskContextType& taskContext = _taskContexts[static_cast<size_t>(context)];
358 6 : if (context == TASK_IDLE)
359 : {
360 1 : _idleTaskInitializer = &initializer;
361 1 : taskContext.initTask(TASK_IDLE, initializer._name, initializer._taskFunction);
362 : }
363 : else
364 : {
365 5 : taskContext.createTask(
366 : context,
367 5 : initializer._task,
368 : initializer._name,
369 : static_cast<UBaseType_t>(context),
370 5 : initializer._stack,
371 : initializer._taskFunction);
372 : }
373 : }
374 8 : }
375 :
376 : template<class Binding>
377 : void FreeRtosAdapter<Binding>::initTask(ContextType const context)
378 : {
379 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
380 : _timeoutManagers[static_cast<size_t>(context)].init(context);
381 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
382 : #ifndef ASYNC_LOOPER_DISABLE
383 : _loopers[static_cast<size_t>(context)].init(context);
384 : #endif // ASYNC_LOOPER_DISABLE
385 : }
386 :
387 : template<class Binding>
388 7 : void FreeRtosAdapter<Binding>::init()
389 : {
390 7 : TaskInitializer::run();
391 : }
392 :
393 : template<class Binding>
394 1 : void FreeRtosAdapter<Binding>::run()
395 : {
396 1 : vTaskStartScheduler();
397 : }
398 :
399 : template<class Binding>
400 1 : void FreeRtosAdapter<Binding>::runningHook()
401 : {
402 1 : _taskContexts[TASK_IDLE].initTaskHandle(xTaskGetIdleTaskHandle());
403 : }
404 :
405 : template<class Binding>
406 2 : bool FreeRtosAdapter<Binding>::getStackUsage(size_t const taskIdx, StackUsage& stackUsage)
407 : {
408 2 : if (taskIdx < FREERTOS_TASK_COUNT)
409 : {
410 2 : stackUsage._stackSize = _stackSizes[taskIdx];
411 2 : uint32_t const unusedSize
412 : = (taskIdx == TASK_TIMER)
413 2 : ? TaskContextType::getUnusedStackSize(xTimerGetTimerDaemonTaskHandle())
414 1 : : _taskContexts[taskIdx].getUnusedStackSize();
415 2 : stackUsage._usedSize = stackUsage._stackSize - unusedSize;
416 2 : return true;
417 : }
418 : return false;
419 : }
420 :
421 : template<class Binding>
422 1 : inline void FreeRtosAdapter<Binding>::callIdleTaskFunction()
423 : {
424 1 : _taskContexts[TASK_IDLE].callTaskFunction();
425 : }
426 :
427 : template<class Binding>
428 2 : inline void FreeRtosAdapter<Binding>::execute(ContextType const context, RunnableType& runnable)
429 : {
430 2 : _taskContexts[static_cast<size_t>(context)].execute(runnable);
431 : }
432 :
433 : template<class Binding>
434 2 : inline void FreeRtosAdapter<Binding>::schedule(
435 : ContextType const context,
436 : RunnableType& runnable,
437 : TimeoutType& timeout,
438 : uint32_t const delay,
439 : TimeUnitType const unit)
440 : {
441 2 : _taskContexts[static_cast<size_t>(context)].schedule(runnable, timeout, delay, unit);
442 : }
443 :
444 : template<class Binding>
445 2 : inline void FreeRtosAdapter<Binding>::scheduleAtFixedRate(
446 : ContextType const context,
447 : RunnableType& runnable,
448 : TimeoutType& timeout,
449 : uint32_t const delay,
450 : TimeUnitType const unit)
451 : {
452 2 : _taskContexts[static_cast<size_t>(context)].scheduleAtFixedRate(runnable, timeout, delay, unit);
453 : }
454 :
455 : template<class Binding>
456 5 : inline void FreeRtosAdapter<Binding>::cancel(TimeoutType& timeout)
457 : {
458 5 : LockType const lock;
459 5 : ContextType const context = timeout._context;
460 5 : if (context != CONTEXT_INVALID)
461 : {
462 4 : timeout._context = CONTEXT_INVALID;
463 9 : _taskContexts[static_cast<size_t>(context)].cancel(timeout);
464 : }
465 5 : }
466 :
467 : #ifndef ASYNC_LOOPER_DISABLE
468 : template<class Binding>
469 : ::loop::looper& FreeRtosAdapter<Binding>::getLooper(ContextType const context)
470 : {
471 : return _loopers[static_cast<size_t>(context)];
472 : }
473 : #endif // ASYNC_LOOPER_DISABLE
474 :
475 : #ifndef ASYNC_TIMEOUTMANAGER2_DISABLE
476 : template<class Binding>
477 : inline ::common::ITimeoutManager2&
478 : FreeRtosAdapter<Binding>::getTimeoutManager(ContextType const context)
479 : {
480 : return _timeoutManagers[static_cast<size_t>(context)];
481 : }
482 : #endif // ASYNC_TIMEOUTMANAGER2_DISABLE
483 :
484 : template<class Binding>
485 32 : inline BaseType_t* FreeRtosAdapter<Binding>::getHigherPriorityTaskWoken()
486 : {
487 32 : return _higherPriorityTaskWoken;
488 : }
489 :
490 : template<class Binding>
491 10 : inline void FreeRtosAdapter<Binding>::enterIsr()
492 : {
493 10 : internal::NestedInterruptLock<> const lock;
494 10 : ++_nestedInterruptCount;
495 10 : _higherPriorityTaskWoken = &FreeRtosAdapter::_higherPriorityTaskWokenFlag;
496 10 : }
497 :
498 : template<class Binding>
499 5 : inline void FreeRtosAdapter<Binding>::leaveIsr()
500 : {
501 5 : internal::NestedInterruptLock<> const lock;
502 5 : --_nestedInterruptCount;
503 5 : if (_nestedInterruptCount == 0U)
504 : {
505 4 : FreeRtosAdapter::_higherPriorityTaskWoken = nullptr;
506 4 : if (FreeRtosAdapter::_higherPriorityTaskWokenFlag != pdFALSE)
507 : {
508 1 : FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
509 1 : portYIELD_FROM_ISR(pdTRUE);
510 : }
511 : }
512 5 : }
513 :
514 : template<class Binding>
515 5 : inline bool FreeRtosAdapter<Binding>::leaveIsrNoYield()
516 : {
517 5 : internal::NestedInterruptLock<> const lock;
518 5 : --_nestedInterruptCount;
519 5 : if (_nestedInterruptCount == 0U)
520 : {
521 4 : FreeRtosAdapter::_higherPriorityTaskWoken = nullptr;
522 4 : if (FreeRtosAdapter::_higherPriorityTaskWokenFlag != pdFALSE)
523 : {
524 2 : FreeRtosAdapter::_higherPriorityTaskWokenFlag = pdFALSE;
525 2 : return true;
526 : }
527 : }
528 : return false;
529 5 : }
530 :
531 : template<class Binding>
532 1 : FreeRtosAdapter<Binding>::StackUsage::StackUsage() : _stackSize(0U), _usedSize(0U)
533 : {}
534 :
535 : namespace internal
536 : {
537 : template<size_t N, typename TaskConfig>
538 : ::estd::array<TaskConfig, N> TaskConfigHolder<N, TaskConfig>::_taskConfigs;
539 :
540 : template<size_t N, typename TaskConfig>
541 1 : void TaskConfigHolder<N, TaskConfig>::setTaskConfig(
542 : size_t const taskIdx, TaskConfigType const& taskConfig)
543 : {
544 1 : _taskConfigs[taskIdx] = taskConfig;
545 : }
546 :
547 : template<size_t N, typename TaskConfig>
548 1 : TaskConfig const* TaskConfigHolder<N, TaskConfig>::getTaskConfig(size_t const taskIdx)
549 : {
550 1 : return &_taskConfigs[taskIdx];
551 : }
552 :
553 : template<size_t N>
554 : void TaskConfigHolder<N, void>::setTaskConfig(
555 : size_t const /*taskIdx*/,
556 : typename TaskConfigHolder<N, void>::TaskConfigType const& /*taskConfig*/)
557 : {}
558 :
559 : template<size_t N>
560 : typename TaskConfigHolder<N, void>::TaskConfigType const*
561 1 : TaskConfigHolder<N, void>::getTaskConfig(size_t const /*taskIdx*/)
562 : {
563 1 : estd_assert(false);
564 : return nullptr;
565 : }
566 :
567 : } // namespace internal
568 :
569 : } // namespace async
570 :
571 : #endif // GUARD_DB9C7111_F66E_4D50_B724_5DF992A19ED0
|