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