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/error_handler.h>
10 : #include <etl/memory.h>
11 : #include <etl/span.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 : ETL_ASSERT(
274 : taskInitializerSlice.size() >= sizeof(TaskInitializer),
275 : ETL_ERROR_GENERIC("stack must be big enough to hold a task initializer object"));
276 18 : new (taskInitializerSlice.data())
277 : TaskInitializer(context, name, task, stackSlice, taskFunction, config);
278 18 : }
279 :
280 : template<class Adapter>
281 18 : TaskInitializer<Adapter>::TaskInitializer(
282 : ContextType const context,
283 : char const* const name,
284 : TaskObjectType& task,
285 : StackSliceType const& stack,
286 : TaskFunctionType const taskFunction,
287 : TaskConfigType const& config)
288 18 : : _stack(stack)
289 18 : , _taskFunction(taskFunction)
290 18 : , _task(task)
291 18 : , _name(name)
292 18 : , _context(context)
293 18 : , _config(config)
294 18 : {}
295 :
296 : template<class Adapter>
297 18 : void TaskInitializer<Adapter>::execute()
298 : {
299 18 : Adapter::initTask(*this);
300 18 : }
301 :
302 : template<class Adapter, ContextType Context, size_t StackSize>
303 14 : TaskImpl<Adapter, Context, StackSize>::TaskImpl(
304 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
305 : {
306 14 : TaskInitializer<Adapter>::create(Context, name, _task, _stack, taskFunction, taskConfig);
307 13 : }
308 :
309 : template<class Adapter, ContextType Context>
310 : template<typename T>
311 5 : TaskImpl<Adapter, Context, 0U>::TaskImpl(
312 : char const* const name,
313 : T& stack,
314 : TaskFunctionType const taskFunction,
315 : TaskConfigType const& taskConfig)
316 : {
317 5 : TaskInitializer<Adapter>::create(Context, name, _task, stack, taskFunction, taskConfig);
318 5 : }
319 :
320 : template<class Adapter, size_t StackSize>
321 1 : IdleTask<Adapter, StackSize>::IdleTask(char const* const name, TaskConfigType const& taskConfig)
322 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE, StackSize>(name, TaskFunctionType(), taskConfig)
323 1 : {}
324 :
325 : template<class Adapter, size_t StackSize>
326 2 : IdleTask<Adapter, StackSize>::IdleTask(
327 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
328 2 : : TaskImpl<Adapter, Adapter::TASK_IDLE, StackSize>(name, taskFunction, taskConfig)
329 2 : {}
330 :
331 : template<class Adapter>
332 : template<typename T>
333 1 : IdleTask<Adapter, 0U>::IdleTask(char const* const name, T& stack, TaskConfigType const& taskConfig)
334 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE>(name, stack, TaskFunctionType(), taskConfig)
335 1 : {}
336 :
337 : template<class Adapter>
338 : template<typename T>
339 1 : IdleTask<Adapter, 0U>::IdleTask(
340 : char const* const name,
341 : T& stack,
342 : TaskFunctionType const taskFunction,
343 : TaskConfigType const& taskConfig)
344 1 : : TaskImpl<Adapter, Adapter::TASK_IDLE>(name, stack, taskFunction, taskConfig)
345 1 : {}
346 :
347 : template<class Adapter, size_t StackSize>
348 2 : TimerTask<Adapter, StackSize>::TimerTask(char const* const name, TaskConfigType const& taskConfig)
349 : : TaskImpl<Adapter, Adapter::TASK_TIMER, StackSize>(
350 2 : name, typename Adapter::TaskFunctionType(), taskConfig)
351 2 : {}
352 :
353 : template<class Adapter>
354 : template<typename T>
355 1 : TimerTask<Adapter, 0U>::TimerTask(
356 : char const* const name, T& stack, TaskConfigType const& taskConfig)
357 : : TaskImpl<Adapter, Adapter::TASK_TIMER>(
358 1 : name, stack, typename Adapter::TaskFunctionType(), taskConfig)
359 1 : {}
360 :
361 : template<class Adapter, ContextType Context, size_t StackSize>
362 5 : Task<Adapter, Context, StackSize>::Task(char const* const name, TaskConfigType const& taskConfig)
363 5 : : TaskImpl<Adapter, Context, StackSize>(name, TaskFunctionType(), taskConfig)
364 4 : {}
365 :
366 : template<class Adapter, ContextType Context, size_t StackSize>
367 4 : Task<Adapter, Context, StackSize>::Task(
368 : char const* const name, TaskFunctionType const taskFunction, TaskConfigType const& taskConfig)
369 4 : : TaskImpl<Adapter, Context, StackSize>(name, taskFunction, taskConfig)
370 4 : {}
371 :
372 : template<class Adapter, ContextType Context>
373 : template<typename T>
374 1 : Task<Adapter, Context, 0U>::Task(char const* const name, T& stack, TaskConfigType const& taskConfig)
375 1 : : TaskImpl<Adapter, Context>(name, stack, typename Adapter::TaskFunctionType(), taskConfig)
376 1 : {}
377 :
378 : template<class Adapter, ContextType Context>
379 : template<typename T>
380 1 : Task<Adapter, Context, 0U>::Task(
381 : char const* const name,
382 : T& stack,
383 : TaskFunctionType const taskFunction,
384 : TaskConfigType const& taskConfig)
385 1 : : TaskImpl<Adapter, Context>(name, stack, taskFunction, taskConfig)
386 1 : {}
387 :
388 : } // namespace internal
389 : } // namespace async
|