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 
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