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
|