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