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 : /**
119 : * Suspends the task such that it is no longer scheduled by FreeRTOS.
120 : */
121 : void suspend();
122 :
123 : /**
124 : * Resumes the task such that it is scheduled by FreeRTOS.
125 : */
126 : void resume();
127 :
128 : /// Calls the task's assigned function.
129 : void callTaskFunction();
130 :
131 : /// Dispatches events for processing within this context.
132 : void dispatch();
133 :
134 : /// Stops event dispatching.
135 : void stopDispatch();
136 :
137 : /// Dispatches events while runnable is executing.
138 : void dispatchWhileWork();
139 :
140 : /**
141 : * Retrieves the amount of unused stack space for a specified task.
142 : * \param taskHandle The handle of the task.
143 : * \return The amount of unused stack space.
144 : */
145 : static uint32_t getUnusedStackSize(TaskHandle_t taskHandle);
146 :
147 : /**
148 : * Default function to be executed by a task within this context.
149 : * \param taskContext The context in which the task executes.
150 : */
151 : static void defaultTaskFunction(TaskContext<Binding>& taskContext);
152 :
153 : /**
154 : * Default function to be executed by the idle task.
155 : * \param taskContext The context in which the task executes.
156 : */
157 : static void defaultIdleFunction(TaskContext<Binding>& taskContext);
158 :
159 : private:
160 : friend class EventPolicy<TaskContext<Binding>, 0U>;
161 : friend class EventPolicy<TaskContext<Binding>, 1U>;
162 :
163 : using ExecuteEventPolicyType = EventPolicy<TaskContext<Binding>, 0U>;
164 : using TimerEventPolicyType = EventPolicy<TaskContext<Binding>, 1U>;
165 : using TimerType = ::timer::Timer<LockType>;
166 :
167 : static EventMaskType const STOP_EVENT_MASK = static_cast<EventMaskType>(
168 : static_cast<EventMaskType>(1U) << static_cast<EventMaskType>(EVENT_COUNT));
169 : static EventMaskType const WAIT_EVENT_MASK = (STOP_EVENT_MASK << 1U) - 1U;
170 :
171 : void setEvents(EventMaskType eventMask);
172 : EventMaskType waitEvents();
173 : EventMaskType peekEvents();
174 :
175 : void handleTimeout();
176 :
177 : static void staticTaskFunction(void* param);
178 :
179 : RunnableExecutor<RunnableType, ExecuteEventPolicyType, LockType> _runnableExecutor;
180 : TimerType _timer;
181 : TimerEventPolicyType _timerEventPolicy;
182 : TaskFunctionType _taskFunction;
183 : TaskHandle_t _taskHandle;
184 : char const* _name;
185 : ContextType _context;
186 : };
187 :
188 : /**
189 : * Inline implementations.
190 : */
191 : template<class Binding>
192 187 : inline TaskContext<Binding>::TaskContext()
193 187 : : _runnableExecutor(*this)
194 187 : , _timerEventPolicy(*this)
195 187 : , _taskFunction()
196 187 : , _taskHandle(nullptr)
197 187 : , _name(nullptr)
198 374 : , _context(CONTEXT_INVALID)
199 : {
200 187 : _timerEventPolicy.setEventHandler(
201 : HandlerFunctionType::create<TaskContext, &TaskContext::handleTimeout>(*this));
202 187 : _runnableExecutor.init();
203 187 : }
204 :
205 : template<class Binding>
206 1 : void TaskContext<Binding>::initTask(
207 : ContextType const context, char const* const name, TaskFunctionType const taskFunction)
208 : {
209 1 : _context = context;
210 1 : _name = name;
211 1 : _taskFunction = taskFunction.is_valid()
212 1 : ? taskFunction
213 0 : : TaskFunctionType::template create<&TaskContext::defaultIdleFunction>();
214 1 : }
215 :
216 : template<class Binding>
217 1 : void TaskContext<Binding>::initTaskHandle(TaskHandle_t const taskHandle)
218 : {
219 1 : _taskHandle = taskHandle;
220 1 : }
221 :
222 : template<class Binding>
223 14 : void TaskContext<Binding>::createTask(
224 : ContextType const context,
225 : StaticTask_t& task,
226 : char const* const name,
227 : UBaseType_t const priority,
228 : StackType const& stack,
229 : TaskFunctionType const taskFunction)
230 : {
231 14 : _context = context;
232 14 : _name = name;
233 14 : _taskFunction = taskFunction.is_valid()
234 14 : ? taskFunction
235 15 : : TaskFunctionType::template create<&TaskContext::defaultTaskFunction>();
236 28 : _taskHandle = xTaskCreateStatic(
237 : &staticTaskFunction,
238 : name,
239 14 : static_cast<uint32_t>(stack.size()),
240 : this,
241 : priority,
242 : stack.data(),
243 : &task);
244 14 : }
245 :
246 : template<class Binding>
247 3 : inline char const* TaskContext<Binding>::getName() const
248 : {
249 3 : return _name;
250 : }
251 :
252 : template<class Binding>
253 1 : inline TaskHandle_t TaskContext<Binding>::getTaskHandle() const
254 : {
255 1 : return _taskHandle;
256 : }
257 :
258 : template<class Binding>
259 2 : inline uint32_t TaskContext<Binding>::getUnusedStackSize() const
260 : {
261 2 : return getUnusedStackSize(_taskHandle);
262 : }
263 :
264 : template<class Binding>
265 5 : inline void TaskContext<Binding>::execute(RunnableType& runnable)
266 : {
267 5 : _runnableExecutor.enqueue(runnable);
268 5 : }
269 :
270 : template<class Binding>
271 6 : inline void TaskContext<Binding>::schedule(
272 : RunnableType& runnable, TimeoutType& timeout, uint32_t const delay, TimeUnitType const unit)
273 : {
274 6 : if (!_timer.isActive(timeout))
275 : {
276 5 : timeout._runnable = &runnable;
277 5 : timeout._context = _context;
278 5 : if (_timer.set(timeout, delay * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
279 : {
280 4 : _timerEventPolicy.setEvent();
281 : }
282 : }
283 6 : }
284 :
285 : template<class Binding>
286 7 : inline void TaskContext<Binding>::scheduleAtFixedRate(
287 : RunnableType& runnable, TimeoutType& timeout, uint32_t const period, TimeUnitType const unit)
288 : {
289 7 : if (!_timer.isActive(timeout))
290 : {
291 6 : timeout._runnable = &runnable;
292 6 : timeout._context = _context;
293 6 : if (_timer.setCyclic(timeout, period * static_cast<uint32_t>(unit), getSystemTimeUs32Bit()))
294 : {
295 5 : _timerEventPolicy.setEvent();
296 : }
297 : }
298 7 : }
299 :
300 : template<class Binding>
301 6 : inline void TaskContext<Binding>::cancel(TimeoutType& timeout)
302 : {
303 6 : _timer.cancel(timeout);
304 6 : }
305 :
306 : template<class Binding>
307 1 : inline void TaskContext<Binding>::suspend()
308 : {
309 1 : if (_taskHandle != nullptr)
310 : {
311 1 : vTaskSuspend(_taskHandle);
312 : }
313 1 : }
314 :
315 : template<class Binding>
316 1 : inline void TaskContext<Binding>::resume()
317 : {
318 1 : if (_taskHandle != nullptr)
319 : {
320 1 : vTaskResume(_taskHandle);
321 : }
322 1 : }
323 :
324 : template<class Binding>
325 22 : inline void TaskContext<Binding>::setEvents(EventMaskType const eventMask)
326 : {
327 22 : BaseType_t* const higherPriorityTaskHasWoken = Binding::getHigherPriorityTaskWoken();
328 22 : if (higherPriorityTaskHasWoken != nullptr)
329 : {
330 1 : xTaskNotifyFromISR(_taskHandle, eventMask, eSetBits, higherPriorityTaskHasWoken);
331 : }
332 : else
333 : {
334 21 : xTaskNotify(_taskHandle, eventMask, eSetBits);
335 : }
336 22 : }
337 :
338 : template<class Binding>
339 25 : inline EventMaskType TaskContext<Binding>::waitEvents()
340 : {
341 25 : EventMaskType eventMask = 0U;
342 25 : uint32_t ticks = Binding::WAIT_EVENTS_TICK_COUNT;
343 : uint32_t nextDelta;
344 25 : bool const hasDelta = _timer.getNextDelta(getSystemTimeUs32Bit(), nextDelta);
345 25 : if (hasDelta)
346 : {
347 14 : ticks = static_cast<uint32_t>((nextDelta + (Config::TICK_IN_US - 1U)) / Config::TICK_IN_US);
348 : }
349 25 : if (xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, ticks) != 0)
350 : {
351 14 : return eventMask;
352 : }
353 11 : else if (hasDelta)
354 : {
355 6 : return TimerEventPolicyType::EVENT_MASK;
356 : }
357 : else
358 : {
359 5 : return 0U;
360 : }
361 : }
362 :
363 : template<class Binding>
364 2 : inline EventMaskType TaskContext<Binding>::peekEvents()
365 : {
366 2 : EventMaskType eventMask = 0U;
367 2 : (void)xTaskNotifyWait(0U, WAIT_EVENT_MASK, &eventMask, 0U);
368 2 : return eventMask;
369 : }
370 :
371 : template<class Binding>
372 10 : void TaskContext<Binding>::callTaskFunction()
373 : {
374 10 : _taskFunction(*this);
375 10 : }
376 :
377 : template<class Binding>
378 8 : void TaskContext<Binding>::dispatch()
379 : {
380 8 : EventMaskType eventMask = 0U;
381 33 : while ((eventMask & STOP_EVENT_MASK) == 0U)
382 : {
383 25 : eventMask = waitEvents();
384 25 : handleEvents(eventMask);
385 : }
386 8 : }
387 :
388 : template<class Binding>
389 8 : inline void TaskContext<Binding>::stopDispatch()
390 : {
391 8 : setEvents(STOP_EVENT_MASK);
392 8 : }
393 :
394 : template<class Binding>
395 1 : void TaskContext<Binding>::dispatchWhileWork()
396 : {
397 1 : while (true)
398 : {
399 2 : handleTimeout();
400 2 : EventMaskType const eventMask = peekEvents();
401 2 : if (eventMask != 0U)
402 : {
403 1 : handleEvents(eventMask);
404 : }
405 : else
406 : {
407 1 : break;
408 : }
409 : }
410 1 : }
411 :
412 : template<class Binding>
413 4 : uint32_t TaskContext<Binding>::getUnusedStackSize(TaskHandle_t const taskHandle)
414 : {
415 4 : return static_cast<uint32_t>(uxTaskGetStackHighWaterMark(taskHandle))
416 4 : * static_cast<uint32_t>(sizeof(StackType_t));
417 : }
418 :
419 : template<class Binding>
420 8 : void TaskContext<Binding>::defaultTaskFunction(TaskContext<Binding>& taskContext)
421 : {
422 8 : taskContext.dispatch();
423 8 : }
424 :
425 : template<class Binding>
426 0 : void TaskContext<Binding>::defaultIdleFunction(TaskContext<Binding>& taskContext)
427 : {
428 0 : taskContext.dispatchWhileWork();
429 0 : }
430 :
431 : template<class Binding>
432 12 : void TaskContext<Binding>::handleTimeout()
433 : {
434 16 : while (_timer.processNextTimeout(getSystemTimeUs32Bit())) {}
435 12 : }
436 :
437 : template<class Binding>
438 9 : void TaskContext<Binding>::staticTaskFunction(void* const param)
439 : {
440 9 : TaskContext& taskContext = *reinterpret_cast<TaskContext*>(param);
441 9 : taskContext.callTaskFunction();
442 9 : }
443 :
444 : } // namespace async
|