F´ Flight Software - C/C++ Documentation  NASA-v1.6.0
A framework for building embedded system applications to NASA flight quality standards.
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 
18 static const NATIVE_INT_TYPE SCHED_POLICY = SCHED_RR;
19 
20 typedef void* (*pthread_func_ptr)(void*);
21 
22 void* 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 
30 namespace 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";
180  tStat = Task::TASK_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 }
Os::set_cpu_affinity
Task::TaskStatus set_cpu_affinity(pthread_attr_t &att, NATIVE_UINT_TYPE cpuAffinity)
Definition: Task.cpp:104
Os
Definition: File.cpp:7
Os::set_stack_size
Task::TaskStatus set_stack_size(pthread_attr_t &att, NATIVE_UINT_TYPE stack)
Definition: Task.cpp:65
Os::Task::TASK_UNKNOWN_ERROR
@ TASK_UNKNOWN_ERROR
unexpected error return value
Definition: Task.hpp:23
Os::Task::TASK_ERROR_PERMISSION
@ TASK_ERROR_PERMISSION
permissions error setting-up tasks
Definition: Task.hpp:28
Fw::StringBase
Definition: StringType.hpp:23
NATIVE_UINT_TYPE
unsigned int NATIVE_UINT_TYPE
native unsigned integer type declaration
Definition: BasicTypes.hpp:28
Os::Task::TASK_ERROR_RESOURCES
@ TASK_ERROR_RESOURCES
unable to allocate more tasks
Definition: Task.hpp:27
pthread_entry_wrapper
void * pthread_entry_wrapper(void *arg)
Definition: Task.cpp:22
Task.hpp
Os::validate_arguments
void validate_arguments(NATIVE_UINT_TYPE &priority, NATIVE_UINT_TYPE &stack, NATIVE_UINT_TYPE &affinity, bool expect_perm)
Definition: Task.cpp:32
Os::Task::Task
Task()
constructor
Definition: Task.cpp:10
T
Definition: T.hpp:9
Os::Task::TaskRoutineWrapper::routine
taskRoutine routine
contains the task entrypoint
Definition: Task.hpp:34
NATIVE_INT_TYPE
int NATIVE_INT_TYPE
native integer type declaration
Definition: BasicTypes.hpp:27
Os::Task::TASK_DEFAULT
static const NATIVE_UINT_TYPE TASK_DEFAULT
Definition: Task.hpp:18
Os::set_priority_params
Task::TaskStatus set_priority_params(pthread_attr_t &att, NATIVE_UINT_TYPE priority)
Definition: Task.cpp:77
Os::Task::TASK_OK
@ TASK_OK
message sent/received okay
Definition: Task.hpp:20
Os::Task::TaskRoutineWrapper
Definition: Task.hpp:33
Os::Task::TaskStatus
TaskStatus
Definition: Task.hpp:19
Os::Task::TASK_INVALID_PARAMS
@ TASK_INVALID_PARAMS
started task with invalid parameters
Definition: Task.hpp:21
FW_ASSERT
#define FW_ASSERT(...)
Definition: Assert.hpp:9
Fw::Logger::logMsg
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
Os::TaskId
Definition: TaskId.hpp:18
Os::Task::TaskRoutineWrapper::arg
void * arg
contains the task entrypoint pointer
Definition: Task.hpp:35
SCHED_POLICY
static const NATIVE_INT_TYPE SCHED_POLICY
Definition: Task.cpp:18
Os::Task::TASK_INVALID_STACK
@ TASK_INVALID_STACK
started with invalid stack size
Definition: Task.hpp:22
Os::create_pthread
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
Logger.hpp