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
|