Line data Source code
1 : // Copyright 2024 Accenture.
2 :
3 : #pragma once
4 :
5 : #include "async/StaticRunnable.h"
6 : #include "async/Types.h"
7 :
8 : #include <etl/array.h>
9 : #include <etl/memory.h>
10 : #include <etl/span.h>
11 : #include <util/estd/assert.h>
12 :
13 : #include <FreeRTOS.h>
14 : #include <task.h>
15 : #include <timers.h>
16 :
17 : namespace async
18 : {
19 : namespace internal
20 : {
21 :
22 : constexpr size_t adjustStackSize(size_t const stackSize)
23 : {
24 : #if (defined(MINIMUM_STACK_SIZE)) && (MINIMUM_STACK_SIZE != 0)
25 : return (stackSize != 0U) && (stackSize < MINIMUM_STACK_SIZE) ? MINIMUM_STACK_SIZE : stackSize;
26 : #else
27 : return stackSize;
28 : #endif // (defined(MINIMUM_STACK_SIZE)) && (MINIMUM_STACK_SIZE != 0)
29 : }
30 :
31 19 : inline void align(size_t const alignment, ::etl::span<uint8_t>& s)
32 : {
33 19 : auto const mod = reinterpret_cast<size_t>(s.data()) % alignment;
34 19 : if (mod != 0)
35 : {
36 0 : s = s.subspan(alignment - mod);
37 : }
38 : }
39 :
40 : template<size_t StackSize>
41 : using Stack = ::etl::array<
42 : StackType_t,
43 : (adjustStackSize(StackSize) + sizeof(StackType_t) - 1) / sizeof(StackType_t)>;
44 :
45 : /**
46 : * The TaskInitializer struct centralizes the initialization of tasks within the application.
47 : *
48 : * This struct provides a standardized way to set up tasks, defining key types and handling
49 : * configuration and setup of task and timer objects. It uses a specified Adapter type
50 : * for flexibility, allowing for different configurations.
51 : *
52 : * \tparam Adapter The adapter type used to provide specific task configuration types and functions.
53 : */
54 : template<typename Adapter>
55 : struct TaskInitializer : public StaticRunnable<TaskInitializer<Adapter>>
56 : {
57 : using AdapterType = Adapter;
58 : using StackSliceType = ::etl::span<StackType_t>;
59 : using TaskConfigType = typename AdapterType::TaskConfigType;
60 : using TaskFunctionType = typename AdapterType::TaskFunctionType;
61 : using TaskObjectType = StaticTask_t;
62 :
63 : /**
64 : * Creates and initializes a task with the specified parameters.
65 : *
66 : * This function handles task creation by allocating memory for the object,
67 : * setting up the context, stack, and configuration,
68 : * and linking them to the specified task and timer objects.
69 : *
70 : * \tparam T The type of the stack used by the task.
71 : * \param context The execution context of the task.
72 : * \param name The name of the task.
73 : * \param task The static task object to initialize.
74 : * \param timer The static timer object associated with the task.
75 : * \param stack The stack to use for the task's execution.
76 : * \param taskFunction The function that the task will execute.
77 : * \param config Configuration settings for the task.
78 : */
79 : template<typename T>
80 : static void create(
81 : ContextType context,
82 : char const* name,
83 : TaskObjectType& task,
84 : T& stack,
85 : TaskFunctionType taskFunction,
86 : TaskConfigType const& config);
87 :
88 : private:
89 : TaskInitializer(
90 : ContextType context,
91 : char const* name,
92 : TaskObjectType& task,
93 : StackSliceType const& stack,
94 : TaskFunctionType taskFunction,
95 : TaskConfigType const& config);
96 :
97 : public:
98 : /// Executes task object initialization.
99 : void execute();
100 :
101 : /// The stack slice used for task execution.
102 : StackSliceType _stack;
103 :
104 : /// The function assigned for the task's execution.
105 : TaskFunctionType _taskFunction;
106 :
107 : /// Reference to the static task object.
108 : TaskObjectType& _task;
109 : /// The name of the task.
110 : char const* _name;
111 :
112 : /// The context in which the task will execute.
113 : ContextType _context;
114 :
115 : /// The configuration settings for the task.
116 : TaskConfigType _config;
117 : };
118 :
119 : /**
120 : * Primary template class that serves as a container for task-related objects and
121 : * configuration.
122 : *
123 : * TaskImpl provides a structure to hold essential components for tasks, such as the stack, task
124 : * object, and timer object, along with configuration settings. It serves as a base for derived task
125 : * classes (e.g., Task, IdleTask) and is designed for integration with FreeRTOS, using the Adapter
126 : * type for flexibility.
127 : *
128 : * \tparam Adapter The adapter type that supplies specific task configuration types and functions.
129 : * \tparam Context The context in which the task operates.
130 : * \tparam StackSize The size of the stack allocated for the task.
131 : */
132 : template<class Adapter, ContextType Context, size_t StackSize = 0U>
133 : class TaskImpl
134 : {
135 : public:
136 : using AdapterType = Adapter;
137 : using StackSliceType = ::etl::span<StackType_t>;
138 : using TaskConfigType = typename AdapterType::TaskConfigType;
139 : using TaskFunctionType = typename AdapterType::TaskFunctionType;
140 : using TaskObjectType = StaticTask_t;
141 :
142 : TaskImpl(char const* name, TaskFunctionType taskFunction, TaskConfigType const& taskConfig);
143 :
144 : protected:
145 : ~TaskImpl() = default;
146 :
147 : private:
148 : Stack<StackSize> _stack;
149 : TaskObjectType _task;
150 : };
151 :
152 : template<class Adapter, ContextType Context>
153 : class TaskImpl<Adapter, Context, 0U>
154 : {
155 : public:
156 : using AdapterType = Adapter;
157 : using StackSliceType = ::etl::span<StackType_t>;
158 : using TaskConfigType = typename AdapterType::TaskConfigType;
159 : using TaskFunctionType = typename AdapterType::TaskFunctionType;
160 : using TaskObjectType = StaticTask_t;
161 :
162 : template<typename T>
163 : TaskImpl(
164 : char const* name,
165 : T& stack,
166 : TaskFunctionType taskFunction,
167 : TaskConfigType const& taskConfig);
168 :
169 : protected:
170 : ~TaskImpl() = default;
171 :
172 : private:
173 : TaskObjectType _task;
174 : };
175 :
176 : template<class Adapter, size_t StackSize = 0U>
177 : struct IdleTask : public TaskImpl<Adapter, Adapter::TASK_IDLE, StackSize>
178 : {
179 : using TaskFunctionType = typename Adapter::TaskFunctionType;
180 : using TaskConfigType = typename Adapter::TaskConfigType;
181 :
182 : IdleTask(char const* name, TaskConfigType const& taskConfig = TaskConfigType());
183 :
184 : IdleTask(
185 : char const* name,
186 : TaskFunctionType taskFunction,
187 : TaskConfigType const& taskConfig = TaskConfigType());
188 : };
189 :
190 : template<class Adapter>
191 : struct IdleTask<Adapter, 0U> : public TaskImpl<Adapter, Adapter::TASK_IDLE>
192 : {
193 : using TaskFunctionType = typename Adapter::TaskFunctionType;
194 : using TaskConfigType = typename Adapter::TaskConfigType;
195 :
196 : template<typename T>
197 : IdleTask(char const* name, T& stack, TaskConfigType const& taskConfig = TaskConfigType());
198 :
199 : template<typename T>
200 : IdleTask(
201 : char const* name,
202 : T& stack,
203 : TaskFunctionType taskFunction,
204 : TaskConfigType const& taskConfig = TaskConfigType());
205 : };
206 :
207 : template<class Adapter, size_t StackSize = 0U>
208 : struct TimerTask : public TaskImpl<Adapter, Adapter::TASK_TIMER, StackSize>
209 : {
210 : using TaskConfigType = typename Adapter::TaskConfigType;
211 :
212 : explicit TimerTask(char const* name, TaskConfigType const& taskConfig = TaskConfigType());
213 : };
214 :
215 : template<class Adapter>
216 : struct TimerTask<Adapter, 0U> : public TaskImpl<Adapter, Adapter::TASK_TIMER>
217 : {
218 : using TaskConfigType = typename Adapter::TaskConfigType;
219 :
220 : template<typename T>
221 : TimerTask(char const* name, T& stack, TaskConfigType const& taskConfig = TaskConfigType());
222 : };
223 :
224 : template<class Adapter, ContextType Context, size_t StackSize = 0U>
225 : struct Task : public TaskImpl<Adapter, Context, StackSize>
226 : {
227 : using TaskFunctionType = typename Adapter::TaskFunctionType;
228 : using TaskConfigType = typename Adapter::TaskConfigType;
229 :
230 : explicit Task(char const* name, TaskConfigType const& taskConfig = TaskConfigType());
231 :
232 : Task(
233 : char const* name,
234 : TaskFunctionType taskFunction,
235 : TaskConfigType const& taskConfig = TaskConfigType());
236 : };
237 :
238 : template<class Adapter, ContextType Context>
239 : struct Task<Adapter, Context, 0U> : public TaskImpl<Adapter, Context>
240 : {
241 : using TaskFunctionType = typename Adapter::TaskFunctionType;
242 : using TaskConfigType = typename Adapter::TaskConfigType;
243 :
244 : template<typename T>
245 : explicit Task(char const* name, T& stack, TaskConfigType const& taskConfig = TaskConfigType());
246 :
247 : template<typename T>
248 : Task(
249 : char const* name,
250 : T& stack,
251 : TaskFunctionType taskFunction,
252 : TaskConfigType const& taskConfig = TaskConfigType());
253 : };
254 :
255 : template<class Adapter>
256 : template<typename T>
257 19 : void TaskInitializer<Adapter>::create(
258 : ContextType const context,
259 : char const* const name,
260 : TaskObjectType& task,
261 : T& stack,
262 : TaskFunctionType const taskFunction,
263 : TaskConfigType const& config)
264 : {
265 19 : ::etl::span<uint8_t> bytes = ::etl::make_span(stack).template reinterpret_as<uint8_t>();
266 19 : align(alignof(StackType_t), bytes);
267 19 : StackSliceType const stackSlice = bytes.template reinterpret_as<StackType_t>();
268 19 : estd_assert((stackSlice.size() * sizeof(StackType_t)) >= sizeof(TaskInitializer));
269 18 : new (stackSlice.data()) TaskInitializer(context, name, task, stackSlice, taskFunction, config);
270 18 : }
271 :
272 : template<class Adapter>
273 18 : TaskInitializer<Adapter>::TaskInitializer(
274 : ContextType const context,
275 : char const* const name,
276 : TaskObjectType& task,
277 : StackSliceType const& stack,
278 : TaskFunctionType const taskFunction,
279 : TaskConfigType const& config)
280 18 : : _stack(stack)
281 18 : , _taskFunction(taskFunction)
282 18 : , _task(task)
283 18 : , _name(name)
284 18 : , _context(context)
285 18 : , _config(config)
286 : {}
287 :
288 : template<class Adapter>
289 18 : void TaskInitializer<Adapter>::execute()
290 : {
291 18 : Adapter::initTask(*this);
292 18 : }
293 :
294 : template<class Adapter, ContextType Context, size_t StackSize>
295 14 : TaskImpl<Adapter, Context, StackSize>::TaskImpl(
296 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
297 : {
298 8 : TaskInitializer<Adapter>::create(Context, name, _task, _stack, taskFunction, taskConfig);
299 : }
300 :
301 : template<class Adapter, ContextType Context>
302 : template<typename T>
303 5 : TaskImpl<Adapter, Context, 0U>::TaskImpl(
304 : char const* const name,
305 : T& stack,
306 : TaskFunctionType const taskFunction,
307 : TaskConfigType const& taskConfig)
308 : {
309 3 : TaskInitializer<Adapter>::create(Context, name, _task, stack, taskFunction, taskConfig);
310 : }
311 :
312 : template<class Adapter, size_t StackSize>
313 1 : IdleTask<Adapter, StackSize>::IdleTask(char const* const name, TaskConfigType const& taskConfig)
314 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE, StackSize>(name, TaskFunctionType(), taskConfig)
315 1 : {}
316 :
317 : template<class Adapter, size_t StackSize>
318 2 : IdleTask<Adapter, StackSize>::IdleTask(
319 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
320 2 : : TaskImpl<Adapter, Adapter::TASK_IDLE, StackSize>(name, taskFunction, taskConfig)
321 2 : {}
322 :
323 : template<class Adapter>
324 : template<typename T>
325 1 : IdleTask<Adapter, 0U>::IdleTask(char const* const name, T& stack, TaskConfigType const& taskConfig)
326 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE>(name, stack, TaskFunctionType(), taskConfig)
327 1 : {}
328 :
329 : template<class Adapter>
330 : template<typename T>
331 1 : IdleTask<Adapter, 0U>::IdleTask(
332 : char const* const name,
333 : T& stack,
334 : TaskFunctionType const taskFunction,
335 : TaskConfigType const& taskConfig)
336 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE>(name, stack, taskFunction, taskConfig)
337 : {}
338 :
339 : template<class Adapter, size_t StackSize>
340 2 : TimerTask<Adapter, StackSize>::TimerTask(char const* const name, TaskConfigType const& taskConfig)
341 : : TaskImpl<Adapter, Adapter::TASK_TIMER, StackSize>(
342 2 : name, typename Adapter::TaskFunctionType(), taskConfig)
343 2 : {}
344 :
345 : template<class Adapter>
346 : template<typename T>
347 1 : TimerTask<Adapter, 0U>::TimerTask(
348 : char const* const name, T& stack, TaskConfigType const& taskConfig)
349 : : TaskImpl<Adapter, Adapter::TASK_TIMER>(
350 1 : name, stack, typename Adapter::TaskFunctionType(), taskConfig)
351 1 : {}
352 :
353 : template<class Adapter, ContextType Context, size_t StackSize>
354 5 : Task<Adapter, Context, StackSize>::Task(char const* const name, TaskConfigType const& taskConfig)
355 5 : : TaskImpl<Adapter, Context, StackSize>(name, TaskFunctionType(), taskConfig)
356 4 : {}
357 :
358 : template<class Adapter, ContextType Context, size_t StackSize>
359 4 : Task<Adapter, Context, StackSize>::Task(
360 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
361 4 : : TaskImpl<Adapter, Context, StackSize>(name, taskFunction, taskConfig)
362 4 : {}
363 :
364 : template<class Adapter, ContextType Context>
365 : template<typename T>
366 1 : Task<Adapter, Context, 0U>::Task(char const* const name, T& stack, TaskConfigType const& taskConfig)
367 1 : : TaskImpl<Adapter, Context>(name, stack, typename Adapter::TaskFunctionType(), taskConfig)
368 1 : {}
369 :
370 : template<class Adapter, ContextType Context>
371 : template<typename T>
372 1 : Task<Adapter, Context, 0U>::Task(
373 : char const* const name,
374 : T& stack,
375 : TaskFunctionType const taskFunction,
376 : TaskConfigType const& taskConfig)
377 1 : : TaskImpl<Adapter, Context>(name, stack, taskFunction, taskConfig)
378 : {}
379 :
380 : } // namespace internal
381 : } // namespace async
|