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