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
|