Line data Source code
1 : /********************************************************************************
2 : * Copyright (c) 2024 Accenture
3 : *
4 : * This program and the accompanying materials are made available under the
5 : * terms of the Apache License Version 2.0 which is available at
6 : * https://www.apache.org/licenses/LICENSE-2.0
7 : *
8 : * SPDX-License-Identifier: Apache-2.0
9 : ********************************************************************************/
10 :
11 : /**
12 : * \ingroup async
13 : */
14 : #pragma once
15 :
16 : #include "async/EventDispatcher.h"
17 : #include "async/EventPolicy.h"
18 : #include "async/RunnableExecutor.h"
19 : #include "async/Types.h"
20 :
21 : #include <bsp/timer/SystemTimer.h>
22 : #include <etl/delegate.h>
23 : #include <etl/span.h>
24 : #include <timer/Timer.h>
25 :
26 : #include <FreeRTOS.h>
27 : #include <task.h>
28 :
29 : namespace async
30 : {
31 : /**
32 : * Provides an interface between application-specific Tasks and Timers
33 : * and the FreeRTOS framework, managing FreeRTOS* task and timer callbacks.
34 : *
35 : * The TaskContext class facilitates seamless integration between the application's
36 : * task and timer logic and FreeRTOS by invoking necessary FreeRTOS functions
37 : * for task creation, scheduling and processing callbacks.
38 : *
39 : * \tparam Binding The specific binding type associated with the TaskContext.
40 : */
41 : template<class Binding>
42 : class TaskContext : public EventDispatcher<2U, LockType>
43 : {
44 : public:
45 : using TaskFunctionType = ::etl::delegate<void(TaskContext<Binding>&)>;
46 : using StackType = ::etl::span<StackType_t>;
47 :
48 : TaskContext();
49 :
50 : /**
51 : * Initializes a task with a specified context and task function.
52 : * \param context The context associated with this task.
53 : * \param name The name of this task.
54 : * \param taskFunction The function to execute in this task.
55 : */
56 : void initTask(ContextType context, char const* const name, TaskFunctionType taskFunction);
57 :
58 : /**
59 : * Sets the FreeRTOS task handle for this context.
60 : * \param taskHandle The handle to associate with the FreeRTOS task.
61 : */
62 : void initTaskHandle(TaskHandle_t taskHandle);
63 :
64 : /**
65 : * Creates a FreeRTOS task with specific configuration.
66 : * \param context The task's context.
67 : * \param task The static task storage structure.
68 : * \param name The name of the task.
69 : * \param priority The priority of the task.
70 : * \param stack The stack allocated for the task.
71 : * \param taskFunction The function that the task will execute.
72 : */
73 : void createTask(
74 : ContextType context,
75 : StaticTask_t& task,
76 : char const* name,
77 : UBaseType_t priority,
78 : StackType const& stack,
79 : TaskFunctionType taskFunction);
80 :
81 : char const* getName() const;
82 :
83 : TaskHandle_t getTaskHandle() const;
84 :
85 : uint32_t getUnusedStackSize() const;
86 :
87 : /**
88 : * Executes asynchronously the specified runnable within this task context.
89 : * \param runnable The runnable to execute.
90 : */
91 : void execute(RunnableType& runnable);
92 :
93 : /**
94 : * Schedules a runnable to execute after a delay.
95 : * \param runnable The runnable to schedule.
96 : * \param timeout The timeout associated with the runnable.
97 : * \param delay The delay before execution.
98 : * \param unit The time unit for the delay.
99 : */
100 : void schedule(RunnableType& runnable, TimeoutType& timeout, uint32_t delay, TimeUnitType unit);
101 :
102 : /**
103 : * Schedules a runnable to execute at a fixed rate.
104 : * \param runnable The runnable to schedule.
105 : * \param timeout The timeout associated with the runnable.
106 : * \param period The period between executions.
107 : * \param unit The time unit for the period.
108 : */
109 : void scheduleAtFixedRate(
110 : RunnableType& runnable, TimeoutType& timeout, uint32_t period, TimeUnitType unit);
111 :
112 : /**
113 : * Cancels a scheduled runnable.
114 : * \param timeout The timeout associated with the runnable to cancel.
115 : */
116 : void cancel(TimeoutType& timeout);
117 :
118 : /// Calls the task's assigned function.
119 : void callTaskFunction();
120 :
121 : /// Dispatches events for processing within this context.
122 : void dispatch();
123 :
124 : /// Stops event dispatching.
125 : void stopDispatch();
126 :
127 : /// Dispatches events while runnable is executing.
128 : void dispatchWhileWork();
129 :
130 : /**
131 : * Retrieves the amount of unused stack space for a specified task.
132 : * \param taskHandle The handle of the task.
133 : * \return The amount of unused stack space.
134 : */
135 : static uint32_t getUnusedStackSize(TaskHandle_t taskHandle);
136 :
137 : /**
138 : * Default function to be executed by a task within this context.
139 : * \param taskContext The context in which the task executes.
140 : */
141 : static void defaultTaskFunction(TaskContext<Binding>& taskContext);
142 :
143 : /**
144 : * Default function to be executed by the idle task.
145 : * \param taskContext The context in which the task executes.
146 : */
147 : static void defaultIdleFunction(TaskContext<Binding>& taskContext);
148 :
149 : private:
150 : friend class EventPolicy<TaskContext<Binding>, 0U>;
151 : friend class EventPolicy<TaskContext<Binding>, 1U>;
152 :
153 : using ExecuteEventPolicyType = EventPolicy<TaskContext<Binding>, 0U>;
154 : using TimerEventPolicyType = EventPolicy<TaskContext<Binding>, 1U>;
155 : using TimerType = ::timer::Timer<LockType>;
156 :
157 : static EventMaskType const STOP_EVENT_MASK = static_cast<EventMaskType>(
158 : static_cast<EventMaskType>(1U) << static_cast<EventMaskType>(EVENT_COUNT));
159 : static EventMaskType const WAIT_EVENT_MASK = (STOP_EVENT_MASK << 1U) - 1U;
160 :
161 : void setEvents(EventMaskType eventMask);
162 : EventMaskType waitEvents();
163 : EventMaskType peekEvents();
164 :
165 : void handleTimeout();
166 :
167 : static void staticTaskFunction(void* param);
168 :
169 : RunnableExecutor<RunnableType, ExecuteEventPolicyType, LockType> _runnableExecutor;
170 : TimerType _timer;
171 : TimerEventPolicyType _timerEventPolicy;
172 : TaskFunctionType _taskFunction;
173 : TaskHandle_t _taskHandle;
174 : char const* _name;
175 : ContextType _context;
176 : };
177 :
178 : /**
179 : * Inline implementations.
180 : */
181 : template<class Binding>
182 181 : inline TaskContext<Binding>::TaskContext()
183 181 : : _runnableExecutor(*this)
184 181 : , _timerEventPolicy(*this)
185 181 : , _taskFunction()
186 181 : , _taskHandle(nullptr)
187 181 : , _name(nullptr)
188 362 : , _context(CONTEXT_INVALID)
189 : {
190 181 : _timerEventPolicy.setEventHandler(
191 : HandlerFunctionType::create<TaskContext, &TaskContext::handleTimeout>(*this));
192 181 : _runnableExecutor.init();
193 181 : }
194 :
195 : template<class Binding>
196 1 : void TaskContext<Binding>::initTask(
197 : ContextType const context, char const* const name, TaskFunctionType const taskFunction)
198 : {
199 1 : _context = context;
200 1 : _name = name;
201 1 : _taskFunction = taskFunction.is_valid()
202 : ? taskFunction
203 0 : : TaskFunctionType::template create<&TaskContext::defaultIdleFunction>();
204 1 : }
205 :
206 : template<class Binding>
207 1 : void TaskContext<Binding>::initTaskHandle(TaskHandle_t const taskHandle)
208 : {
209 1 : _taskHandle = taskHandle;
210 1 : }
211 :
212 : template<class Binding>
213 13 : void TaskContext<Binding>::createTask(
214 : ContextType const context,
215 : StaticTask_t& task,
216 : char const* const name,
217 : UBaseType_t const priority,
218 : StackType const& stack,
219 : TaskFunctionType const taskFunction)
220 : {
221 13 : _context = context;
222 13 : _name = name;
223 13 : _taskFunction = taskFunction.is_valid()
224 : ? taskFunction
225 15 : : TaskFunctionType::template create<&TaskContext::defaultTaskFunction>();
226 26 : _taskHandle = xTaskCreateStatic(
227 : &staticTaskFunction,
228 : name,
229 13 : static_cast<uint32_t>(stack.size()),
230 : this,
231 : priority,
232 : stack.data(),
233 : &task);
234 13 : }
235 :
236 : template<class Binding>
237 3 : inline char const* TaskContext<Binding>::getName() const
238 : {
239 3 : return _name;
240 : }
241 :
242 : template<class Binding>
243 1 : inline TaskHandle_t TaskContext<Binding>::getTaskHandle() const
244 : {
245 1 : return _taskHandle;
246 : }
247 :
248 : template<class Binding>
249 2 : inline uint32_t TaskContext<Binding>::getUnusedStackSize() const
250 : {
251 2 : return getUnusedStackSize(_taskHandle);
252 : }
253 :
254 : template<class Binding>
255 5 : inline void TaskContext<Binding>::execute(RunnableType& runnable)
256 : {
257 5 : _runnableExecutor.enqueue(runnable);
258 5 : }
259 :
260 : template<class Binding>
261 6 : inline void TaskContext<Binding>::schedule(
262 : RunnableType& runnable, TimeoutType& timeout, uint32_t const delay, TimeUnitType const unit)
263 : {
264 6 : if (!_timer.isActive(timeout))
265 : {
266 5 : timeout._runnable = &runnable;
267 5 : timeout._context = _context;
268 5 : if (_timer.set(timeout, delay * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
269 : {
270 4 : _timerEventPolicy.setEvent();
271 : }
272 : }
273 6 : }
274 :
275 : template<class Binding>
276 7 : inline void TaskContext<Binding>::scheduleAtFixedRate(
277 : RunnableType& runnable, TimeoutType& timeout, uint32_t const period, TimeUnitType const unit)
278 : {
279 7 : if (!_timer.isActive(timeout))
280 : {
281 6 : timeout._runnable = &runnable;
282 6 : timeout._context = _context;
283 6 : if (_timer.setCyclic(timeout, period * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
284 : {
285 5 : _timerEventPolicy.setEvent();
286 : }
287 : }
288 7 : }
289 :
290 : template<class Binding>
291 6 : inline void TaskContext<Binding>::cancel(TimeoutType& timeout)
292 : {
293 6 : _timer.cancel(timeout);
294 6 : }
295 :
296 : template<class Binding>
297 22 : inline void TaskContext<Binding>::setEvents(EventMaskType const eventMask)
298 : {
299 22 : BaseType_t* const higherPriorityTaskHasWoken = Binding::getHigherPriorityTaskWoken();
300 22 : if (higherPriorityTaskHasWoken != nullptr)
301 : {
302 1 : xTaskNotifyFromISR(_taskHandle, eventMask, eSetBits, higherPriorityTaskHasWoken);
303 : }
304 : else
305 : {
306 21 : xTaskNotify(_taskHandle, eventMask, eSetBits);
307 : }
308 22 : }
309 :
310 : template<class Binding>
311 25 : inline EventMaskType TaskContext<Binding>::waitEvents()
312 : {
313 25 : EventMaskType eventMask = 0U;
314 25 : uint32_t ticks = Binding::WAIT_EVENTS_TICK_COUNT;
315 : uint32_t nextDelta;
316 25 : bool const hasDelta = _timer.getNextDelta(getSystemTimeUs32Bit(), nextDelta);
317 25 : if (hasDelta)
318 : {
319 14 : ticks = static_cast<uint32_t>((nextDelta + (Config::TICK_IN_US - 1U)) / Config::TICK_IN_US);
320 : }
321 25 : if (xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, ticks) != 0)
322 : {
323 14 : return eventMask;
324 : }
325 11 : else if (hasDelta)
326 : {
327 6 : return TimerEventPolicyType::EVENT_MASK;
328 : }
329 : else
330 : {
331 5 : return 0U;
332 : }
333 : }
334 :
335 : template<class Binding>
336 2 : inline EventMaskType TaskContext<Binding>::peekEvents()
337 : {
338 2 : EventMaskType eventMask = 0U;
339 2 : (void)xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, 0U);
340 2 : return eventMask;
341 : }
342 :
343 : template<class Binding>
344 10 : void TaskContext<Binding>::callTaskFunction()
345 : {
346 10 : _taskFunction(*this);
347 10 : }
348 :
349 : template<class Binding>
350 8 : void TaskContext<Binding>::dispatch()
351 : {
352 8 : EventMaskType eventMask = 0U;
353 33 : while ((eventMask & STOP_EVENT_MASK) == 0U)
354 : {
355 25 : eventMask = waitEvents();
356 25 : handleEvents(eventMask);
357 : }
358 8 : }
359 :
360 : template<class Binding>
361 8 : inline void TaskContext<Binding>::stopDispatch()
362 : {
363 8 : setEvents(STOP_EVENT_MASK);
364 8 : }
365 :
366 : template<class Binding>
367 2 : void TaskContext<Binding>::dispatchWhileWork()
368 : {
369 1 : while (true)
370 : {
371 2 : handleTimeout();
372 2 : EventMaskType const eventMask = peekEvents();
373 2 : if (eventMask != 0U)
374 : {
375 1 : handleEvents(eventMask);
376 : }
377 : else
378 : {
379 1 : break;
380 : }
381 : }
382 1 : }
383 :
384 : template<class Binding>
385 4 : uint32_t TaskContext<Binding>::getUnusedStackSize(TaskHandle_t const taskHandle)
386 : {
387 4 : return static_cast<uint32_t>(uxTaskGetStackHighWaterMark(taskHandle))
388 4 : * static_cast<uint32_t>(sizeof(StackType_t));
389 : }
390 :
391 : template<class Binding>
392 8 : void TaskContext<Binding>::defaultTaskFunction(TaskContext<Binding>& taskContext)
393 : {
394 8 : taskContext.dispatch();
395 8 : }
396 :
397 : template<class Binding>
398 0 : void TaskContext<Binding>::defaultIdleFunction(TaskContext<Binding>& taskContext)
399 : {
400 0 : taskContext.dispatchWhileWork();
401 0 : }
402 :
403 : template<class Binding>
404 12 : void TaskContext<Binding>::handleTimeout()
405 : {
406 16 : while (_timer.processNextTimeout(getSystemTimeUs32Bit())) {}
407 12 : }
408 :
409 : template<class Binding>
410 9 : void TaskContext<Binding>::staticTaskFunction(void* const param)
411 : {
412 9 : TaskContext& taskContext = *reinterpret_cast<TaskContext*>(param);
413 9 : taskContext.callTaskFunction();
414 9 : }
415 :
416 : } // namespace async
|