F´ Flight Software - C/C++ Documentation  devel
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  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