F´ Flight Software - C/C++ Documentation NASA-v1.6.0
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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