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
|