F´ Flight Software - C/C++ Documentation devel
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 delete tid;
186 tid = nullptr;
187 Fw::Logger::logMsg("pthread_create: %s. %s\n", reinterpret_cast<POINTER_CAST>(message), reinterpret_cast<POINTER_CAST>(strerror(stat)));
188 return tStat;
189 }
190 return Task::TASK_OK;
191 }
192
193 Task::Task() : m_handle(reinterpret_cast<POINTER_CAST>(nullptr)), m_identifier(0), m_affinity(-1), m_started(false), m_suspendedOnPurpose(false), m_routineWrapper() {
194 }
195
196 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) {
197 FW_ASSERT(routine);
198
199 this->m_name = "TP_";
200 this->m_name += name;
201 this->m_identifier = identifier;
202 // Setup functor wrapper parameters
203 this->m_routineWrapper.routine = routine;
204 this->m_routineWrapper.arg = arg;
205 pthread_t* tid;
206
207 // Try to create thread with assuming permissions
208 TaskStatus status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, true);
209 // Failure due to permission automatically retried
210 if (status == TASK_ERROR_PERMISSION) {
211 Fw::Logger::logMsg("[WARNING] Insufficient Permissions:\n");
212 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()));
213 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");
214 Fw::Logger::logMsg("[WARNING] Note: future releases of fprime will fail when setting priority/affinity without sufficient permissions \n");
215 Fw::Logger::logMsg("\n");
216 status = create_pthread(priority, stackSize, cpuAffinity, tid, &this->m_routineWrapper, false); // Fallback with no permission
217 }
218 // Check for non-zero error code
219 if (status != TASK_OK) {
220 return status;
221 }
222 FW_ASSERT(tid != nullptr);
223
224 // Handle a successfully created task
225 this->m_handle = reinterpret_cast<POINTER_CAST>(tid);
226 Task::s_numTasks++;
227 // If a registry has been registered, register task
228 if (Task::s_taskRegistry) {
229 Task::s_taskRegistry->addTask(this);
230 }
231 return status;
232 }
233
234 Task::TaskStatus Task::delay(NATIVE_UINT_TYPE milliseconds)
235 {
236 timespec time1;
237
238 time1.tv_sec = milliseconds/1000;
239 time1.tv_nsec = (milliseconds%1000)*1000000;
240
241 timespec time2;
242 time2.tv_sec = 0;
243 time2.tv_nsec = 0;
244
245 timespec* sleepTimePtr = &time1;
246 timespec* remTimePtr = &time2;
247
248 while (true) {
249 int stat = nanosleep(sleepTimePtr,remTimePtr);
250 if (0 == stat) {
251 return TASK_OK;
252 } else { // check errno
253 if (EINTR == errno) { // swap pointers
254 timespec* temp = remTimePtr;
255 remTimePtr = sleepTimePtr;
256 sleepTimePtr = temp;
257 continue; // if interrupted, just continue
258 } else {
259 return TASK_DELAY_ERROR;
260 }
261 }
262 }
263 return TASK_OK; // for coverage analysis
264 }
265
266
267 Task::~Task() {
268 if (this->m_handle) {
269 delete reinterpret_cast<pthread_t*>(this->m_handle);
270 }
271 // If a registry has been registered, remove task
272 if (Task::s_taskRegistry) {
273 Task::s_taskRegistry->removeTask(this);
274 }
275
276 }
277
278 // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
279 // is not top-level pthreads support for suspend and resume.
280
281 void Task::suspend(bool onPurpose) {
282 FW_ASSERT(0);
283 }
284
285 void Task::resume() {
286 FW_ASSERT(0);
287 }
288
289 bool Task::isSuspended() {
290 FW_ASSERT(0);
291 return false;
292 }
293
294 TaskId Task::getOsIdentifier() {
295 TaskId T;
296 return T;
297 }
298
299 Task::TaskStatus Task::join(void **value_ptr) {
300 NATIVE_INT_TYPE stat = 0;
301 if (!(this->m_handle)) {
302 return TASK_JOIN_ERROR;
303 }
304 stat = pthread_join(*reinterpret_cast<pthread_t*>(this->m_handle), value_ptr);
305
306 if (stat != 0) {
307 return TASK_JOIN_ERROR;
308 }
309 else {
310 return TASK_OK;
311 }
312 }
313}
#define FW_ASSERT(...)
Definition Assert.hpp:14
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