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