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