F´ Flight Software - C/C++ Documentation  NASA-v2.1.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 #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