F´ Flight Software - C/C++ Documentation NASA-v1.6.0
A framework for building embedded system applications to NASA flight quality standards.
Loading...
Searching...
No Matches
Task.cpp
Go to the documentation of this file.
1#include <Os/Task.hpp>
2#include <Fw/Types/Assert.hpp>
3
4#include <pthread.h>
5#include <cerrno>
6#include <cstring>
7#include <ctime>
8#include <cstdio>
9#include <new>
10#include <sched.h>
11#include <climits>
12#include <Fw/Logger/Logger.hpp>
13
14#ifdef TGT_OS_TYPE_LINUX
15#include <features.h>
16#endif
17
18static const NATIVE_INT_TYPE SCHED_POLICY = SCHED_RR;
19
20typedef void* (*pthread_func_ptr)(void*);
21
22void* pthread_entry_wrapper(void* arg) {
23 FW_ASSERT(arg);
24 Os::Task::TaskRoutineWrapper *task = reinterpret_cast<Os::Task::TaskRoutineWrapper*>(arg);
25 FW_ASSERT(task->routine);
26 task->routine(task->arg);
27 return nullptr;
28}
29
30namespace Os {
31
32 void validate_arguments(NATIVE_UINT_TYPE& priority, NATIVE_UINT_TYPE& stack, NATIVE_UINT_TYPE& affinity, bool expect_perm) {
33 const NATIVE_INT_TYPE min_priority = sched_get_priority_min(SCHED_POLICY);
34 const NATIVE_INT_TYPE max_priority = sched_get_priority_max(SCHED_POLICY);
35 // Check to ensure that these calls worked. -1 is an error
36 if (min_priority < 0 or max_priority < 0) {
37 Fw::Logger::logMsg("[WARNING] Unable to determine min/max priority with error %s. Discarding priority.\n", reinterpret_cast<POINTER_CAST>(strerror(errno)));
38 priority = Os::Task::TASK_DEFAULT;
39 }
40 // Check priority attributes
41 if (!expect_perm and priority != Task::TASK_DEFAULT) {
42 Fw::Logger::logMsg("[WARNING] Task priority set and permissions unavailable. Discarding priority.\n");
43 priority = Task::TASK_DEFAULT; //Action: use constant
44 }
45 if (priority != Task::TASK_DEFAULT and priority < static_cast<NATIVE_UINT_TYPE>(min_priority)) {
46 Fw::Logger::logMsg("[WARNING] Low task priority of %d being clamped to %d\n", priority, min_priority);
47 priority = min_priority;
48 }
49 if (priority != Task::TASK_DEFAULT and priority > static_cast<NATIVE_UINT_TYPE>(max_priority)) {
50 Fw::Logger::logMsg("[WARNING] High task priority of %d being clamped to %d\n", priority, max_priority);
51 priority = max_priority;
52 }
53 // Check the stack
54 if (stack != Task::TASK_DEFAULT and stack < PTHREAD_STACK_MIN) {
55 Fw::Logger::logMsg("[WARNING] Stack size %d too small, setting to minimum of %d\n", stack, PTHREAD_STACK_MIN);
56 stack = PTHREAD_STACK_MIN;
57 }
58 // Check CPU affinity
59 if (!expect_perm and affinity != Task::TASK_DEFAULT) {
60 Fw::Logger::logMsg("[WARNING] Cpu affinity set and permissions unavailable. Discarding affinity.\n");
61 affinity = Task::TASK_DEFAULT;
62 }
63 }
64
65 Task::TaskStatus set_stack_size(pthread_attr_t& att, NATIVE_UINT_TYPE stack) {
66 // Set the stack size, if it has been supplied
67 if (stack != Task::TASK_DEFAULT) {
68 I32 stat = pthread_attr_setstacksize(&att, stack);
69 if (stat != 0) {
70 Fw::Logger::logMsg("pthread_attr_setstacksize: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
72 }
73 }
74 return Task::TASK_OK;
75 }
76
77 Task::TaskStatus set_priority_params(pthread_attr_t& att, NATIVE_UINT_TYPE priority) {
78 if (priority != Task::TASK_DEFAULT) {
79 I32 stat = pthread_attr_setschedpolicy(&att, SCHED_POLICY);
80 if (stat != 0) {
81 Fw::Logger::logMsg("pthread_attr_setschedpolicy: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
83 }
84
85 stat = pthread_attr_setinheritsched(&att, PTHREAD_EXPLICIT_SCHED);
86 if (stat != 0) {
87 Fw::Logger::logMsg("pthread_attr_setinheritsched: %s\n",
88 reinterpret_cast<POINTER_CAST>(strerror(stat)));
90 }
91
92 sched_param schedParam;
93 memset(&schedParam, 0, sizeof(sched_param));
94 schedParam.sched_priority = priority;
95 stat = pthread_attr_setschedparam(&att, &schedParam);
96 if (stat != 0) {
97 Fw::Logger::logMsg("pthread_attr_setschedparam: %s\n", reinterpret_cast<POINTER_CAST>(strerror(stat)));
99 }
100 }
101 return Task::TASK_OK;
102 }
103
104 Task::TaskStatus set_cpu_affinity(pthread_attr_t& att, NATIVE_UINT_TYPE cpuAffinity) {
105 if (cpuAffinity != Task::TASK_DEFAULT) {
106#if TGT_OS_TYPE_LINUX && __GLIBC__
107 cpu_set_t cpuset;
108 CPU_ZERO(&cpuset);
109 CPU_SET(cpuAffinity, &cpuset);
110
111 I32 stat = pthread_attr_setaffinity_np(&att, sizeof(cpu_set_t), &cpuset);
112 if (stat != 0) {
113 Fw::Logger::logMsg("pthread_setaffinity_np: %i %s\n", cpuAffinity,
114 reinterpret_cast<POINTER_CAST>(strerror(stat)));
116 }
117#elif TGT_OS_TYPE_LINUX
118 Fw::Logger::logMsg("[WARNING] Setting CPU affinity is only available on Linux with glibc\n");
119#else
120 Fw::Logger::logMsg("[WARNING] Setting CPU affinity is only available on Linux\n");
121#endif
122 }
123 return Task::TASK_OK;
124 }
125
126 Task::TaskStatus create_pthread(NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity, pthread_t*& tid, void* arg, bool expect_perm) {
128 validate_arguments(priority, stackSize, cpuAffinity, expect_perm);
129 pthread_attr_t att;
130 memset(&att,0, sizeof(att));
131
132
133 I32 stat = pthread_attr_init(&att);
134 if (stat != 0) {
135 Fw::Logger::logMsg("pthread_attr_init: (%d): %s\n", stat, reinterpret_cast<POINTER_CAST>(strerror(stat)));
137 }
138
139 // Handle setting stack size
140 tStat = set_stack_size(att, stackSize);
141 if (tStat != Task::TASK_OK) {
142 return tStat;
143 }
144
145
146 // Handle non-zero priorities
147 tStat = set_priority_params(att, priority);
148 if (tStat != Task::TASK_OK) {
149 return tStat;
150 }
151
152 // Set affinity before creating thread:
153 tStat = set_cpu_affinity(att, cpuAffinity);
154 if (tStat != Task::TASK_OK) {
155 return tStat;
156 }
157
158 tid = new pthread_t;
159 const char* message = nullptr;
160
161 stat = pthread_create(tid, &att, pthread_entry_wrapper, arg);
162 switch (stat) {
163 // Success, do nothing
164 case 0:
165 break;
166 case EINVAL:
167 message = "Invalid thread attributes specified";
169 break;
170 case EPERM:
171 message = "Insufficient permissions to create thread. May not set thread priority without permission";
173 break;
174 case EAGAIN:
175 message = "Unable to allocate thread. Increase thread ulimit.";
177 break;
178 default:
179 message = "Unknown error";
181 break;
182 }
183 (void)pthread_attr_destroy(&att);
184 if (stat != 0) {
185 (void)pthread_join(*tid, nullptr);
186 delete tid;
187 tid = nullptr;
188 Fw::Logger::logMsg("pthread_create: %s. %s\n", reinterpret_cast<POINTER_CAST>(message), reinterpret_cast<POINTER_CAST>(strerror(stat)));
189 return tStat;
190 }
191 return Task::TASK_OK;
192 }
193
194 Task::Task() : m_handle(reinterpret_cast<POINTER_CAST>(nullptr)), m_identifier(0), m_affinity(-1), m_started(false), m_suspendedOnPurpose(false), m_routineWrapper() {
195 }
196
197 Task::TaskStatus Task::start(const Fw::StringBase &name, taskRoutine routine, void* arg, NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity, NATIVE_UINT_TYPE identifier) {
198 FW_ASSERT(routine);
199
200 this->m_name = "TP_";
201 this->m_name += name;
202 this->m_identifier = identifier;
203 // Setup functor wrapper parameters
204 this->m_routineWrapper.routine = routine;
205 this->m_routineWrapper.arg = arg;
206 pthread_t* tid;
207
208 // Try to create thread with assuming permissions
209 TaskStatus status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, true);
210 // Failure due to permission automatically retried
211 if (status == TASK_ERROR_PERMISSION) {
212 Fw::Logger::logMsg("[WARNING] Insufficient Permissions:\n");
213 Fw::Logger::logMsg("[WARNING] Insufficient permissions to set task priority or set task CPU affinity on task %s. Creating task without priority nor affinity.\n", reinterpret_cast<POINTER_CAST>(m_name.toChar()));
214 Fw::Logger::logMsg("[WARNING] Please use no-argument <component>.start() calls, set priority/affinity to TASK_DEFAULT or ensure user has correct permissions for operating system.\n");
215 Fw::Logger::logMsg("[WARNING] Note: future releases of fprime will fail when setting priority/affinity without sufficient permissions \n");
216 Fw::Logger::logMsg("\n");
217 status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, false); // Fallback with no permission
218 }
219 // Check for non-zero error code
220 if (status != TASK_OK) {
221 return status;
222 }
223 FW_ASSERT(tid != nullptr);
224
225 // Handle a successfully created task
226 this->m_handle = reinterpret_cast<POINTER_CAST>(tid);
227 Task::s_numTasks++;
228 // If a registry has been registered, register task
229 if (Task::s_taskRegistry) {
230 Task::s_taskRegistry->addTask(this);
231 }
232 return status;
233 }
234
235 Task::TaskStatus Task::delay(NATIVE_UINT_TYPE milliseconds)
236 {
237 timespec time1;
238
239 time1.tv_sec = milliseconds/1000;
240 time1.tv_nsec = (milliseconds%1000)*1000000;
241
242 timespec time2;
243 time2.tv_sec = 0;
244 time2.tv_nsec = 0;
245
246 timespec* sleepTimePtr = &time1;
247 timespec* remTimePtr = &time2;
248
249 while (true) {
250 int stat = nanosleep(sleepTimePtr,remTimePtr);
251 if (0 == stat) {
252 return TASK_OK;
253 } else { // check errno
254 if (EINTR == errno) { // swap pointers
255 timespec* temp = remTimePtr;
256 remTimePtr = sleepTimePtr;
257 sleepTimePtr = temp;
258 continue; // if interrupted, just continue
259 } else {
260 return TASK_DELAY_ERROR;
261 }
262 }
263 }
264 return TASK_OK; // for coverage analysis
265 }
266
267
268 Task::~Task() {
269 if (this->m_handle) {
270 delete reinterpret_cast<pthread_t*>(this->m_handle);
271 }
272 // If a registry has been registered, remove task
273 if (Task::s_taskRegistry) {
274 Task::s_taskRegistry->removeTask(this);
275 }
276
277 }
278
279 // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
280 // is not top-level pthreads support for suspend and resume.
281
282 void Task::suspend(bool onPurpose) {
283 FW_ASSERT(0);
284 }
285
286 void Task::resume() {
287 FW_ASSERT(0);
288 }
289
290 bool Task::isSuspended() {
291 FW_ASSERT(0);
292 return false;
293 }
294
295 TaskId Task::getOsIdentifier() {
296 TaskId T;
297 return T;
298 }
299
300 Task::TaskStatus Task::join(void **value_ptr) {
301 NATIVE_INT_TYPE stat = 0;
302 if (!(this->m_handle)) {
303 return TASK_JOIN_ERROR;
304 }
305 stat = pthread_join(*reinterpret_cast<pthread_t*>(this->m_handle), value_ptr);
306
307 if (stat != 0) {
308 return TASK_JOIN_ERROR;
309 }
310 else {
311 return TASK_OK;
312 }
313 }
314}
#define FW_ASSERT(...)
Definition Assert.hpp:7
PlatformPointerCastType POINTER_CAST
Definition BasicTypes.h:53
PlatformIntType NATIVE_INT_TYPE
Definition BasicTypes.h:51
PlatformUIntType NATIVE_UINT_TYPE
Definition BasicTypes.h:52
void * pthread_entry_wrapper(void *arg)
Definition Task.cpp:22
static const NATIVE_INT_TYPE SCHED_POLICY
Definition Task.cpp:18
static void logMsg(const char *fmt, POINTER_CAST a0=0, POINTER_CAST a1=0, POINTER_CAST a2=0, POINTER_CAST a3=0, POINTER_CAST a4=0, POINTER_CAST a5=0, POINTER_CAST a6=0, POINTER_CAST a7=0, POINTER_CAST a8=0, POINTER_CAST a9=0)
Definition Logger.cpp:18
Task()
constructor
Definition Task.cpp:10
static const NATIVE_UINT_TYPE TASK_DEFAULT
Definition Task.hpp:17
TaskStatus
Definition Task.hpp:18
@ TASK_ERROR_PERMISSION
permissions error setting-up tasks
Definition Task.hpp:27
@ TASK_OK
message sent/received okay
Definition Task.hpp:19
@ TASK_INVALID_STACK
started with invalid stack size
Definition Task.hpp:21
@ TASK_INVALID_PARAMS
started task with invalid parameters
Definition Task.hpp:20
@ TASK_ERROR_RESOURCES
unable to allocate more tasks
Definition Task.hpp:26
@ TASK_UNKNOWN_ERROR
unexpected error return value
Definition Task.hpp:22
Definition File.cpp:6
void validate_arguments(NATIVE_UINT_TYPE &priority, NATIVE_UINT_TYPE &stack, NATIVE_UINT_TYPE &affinity, bool expect_perm)
Definition Task.cpp:32
Task::TaskStatus set_cpu_affinity(pthread_attr_t &att, NATIVE_UINT_TYPE cpuAffinity)
Definition Task.cpp:104
Task::TaskStatus set_stack_size(pthread_attr_t &att, NATIVE_UINT_TYPE stack)
Definition Task.cpp:65
Task::TaskStatus set_priority_params(pthread_attr_t &att, NATIVE_UINT_TYPE priority)
Definition Task.cpp:77
Task::TaskStatus create_pthread(NATIVE_UINT_TYPE priority, NATIVE_UINT_TYPE stackSize, NATIVE_UINT_TYPE cpuAffinity, pthread_t *&tid, void *arg, bool expect_perm)
Definition Task.cpp:126
taskRoutine routine
contains the task entrypoint
Definition Task.hpp:33
void * arg
contains the task entrypoint pointer
Definition Task.hpp:34