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 // ======================================================================
2 // \title Os/Posix/Task.cpp
3 // \brief implementation of Posix implementation of Os::Task
4 // ======================================================================
5 #include <cstring>
6 #include <unistd.h>
7 #include <climits>
8 #include <cerrno>
9 #include <pthread.h>
10 
11 #include "Fw/Logger/Logger.hpp"
12 #include "Fw/Types/Assert.hpp"
13 #include "Os/Task.hpp"
14 #include "Os/Posix/Task.hpp"
15 #include "Os/Posix/error.hpp"
16 
17 namespace Os {
18 namespace Posix {
19 namespace Task {
20  std::atomic<bool> PosixTask::s_permissions_reported(false);
21  static const PlatformIntType SCHED_POLICY = SCHED_RR;
22 
23  typedef void* (*pthread_func_ptr)(void*);
24 
25  void* pthread_entry_wrapper(void* wrapper_pointer) {
26  FW_ASSERT(wrapper_pointer != nullptr);
27  Os::Task::TaskRoutineWrapper& wrapper = *reinterpret_cast<Os::Task::TaskRoutineWrapper*>(wrapper_pointer);
28  wrapper.run(&wrapper);
29  return nullptr;
30  }
31 
32  PlatformIntType set_stack_size(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
34  FwSizeType stack = arguments.m_stackSize;
35  // Check for stack size multiple of page size
36  long page_size = sysconf(_SC_PAGESIZE);
37  if (page_size <= 0) {
39  "[WARNING] %s could not determine page size %s. Skipping stack-size check.\n",
40  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
41  reinterpret_cast<PlatformPointerCastType>(strerror(errno))
42  );
43  }
44  else if ((stack % static_cast<FwSizeType>(page_size)) != 0) {
45  // Round-down to nearest page size multiple
46  FwSizeType rounded = (stack / static_cast<FwSizeType>(page_size)) * static_cast<FwSizeType>(page_size);
48  "[WARNING] %s stack size of %" PRI_FwSizeType " is not multiple of page size %ld, rounding to %" PRI_FwSizeType "\n",
49  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
50  static_cast<PlatformPointerCastType>(stack),
51  static_cast<PlatformPointerCastType>(page_size),
52  static_cast<PlatformPointerCastType>(rounded)
53  );
54  stack = rounded;
55  }
56 
57  // Clamp invalid stack sizes
58  if (stack <= static_cast<FwSizeType>(PTHREAD_STACK_MIN)) {
60  "[WARNING] %s stack size of %" PRI_FwSizeType " is too small, clamping to %" PRI_FwSizeType "\n",
61  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
62  static_cast<PlatformPointerCastType>(stack),
63  static_cast<PlatformPointerCastType>(static_cast<FwSizeType>(PTHREAD_STACK_MIN))
64  );
65  stack = static_cast<FwSizeType>(PTHREAD_STACK_MIN);
66  }
67  status = pthread_attr_setstacksize(&attributes, static_cast<size_t>(stack));
68  return status;
69  }
70 
71  PlatformIntType set_priority_params(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
72  const FwSizeType min_priority = static_cast<FwSizeType>(sched_get_priority_min(SCHED_POLICY));
73  const FwSizeType max_priority = static_cast<FwSizeType>(sched_get_priority_max(SCHED_POLICY));
75  FwSizeType priority = arguments.m_priority;
76  // Clamp to minimum priority
77  if (priority < min_priority) {
78  Fw::Logger::logMsg("[WARNING] %s low task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
79  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
80  static_cast<PlatformPointerCastType>(priority),
81  static_cast<PlatformPointerCastType>(min_priority));
82  priority = min_priority;
83  }
84  // Clamp to maximum priority
85  else if (priority > max_priority) {
86  Fw::Logger::logMsg("[WARNING] %s high task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
87  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())),
88  static_cast<PlatformPointerCastType>(priority),
89  static_cast<PlatformPointerCastType>(max_priority));
90  priority = max_priority;
91  }
92 
93  // Set attributes required for priority
94  status = pthread_attr_setschedpolicy(&attributes, SCHED_POLICY);
95  if (status == PosixTaskHandle::SUCCESS) {
96  status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
97  }
98  if (status == PosixTaskHandle::SUCCESS) {
99  sched_param schedParam;
100  memset(&schedParam, 0, sizeof(sched_param));
101  schedParam.sched_priority = static_cast<PlatformIntType>(priority);
102  status = pthread_attr_setschedparam(&attributes, &schedParam);
103  }
104  return status;
105  }
106 
107  PlatformIntType set_cpu_affinity(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
108  PlatformIntType status = 0;
109 // Feature set check for _GNU_SOURCE before using GNU only features
110 #ifdef _GNU_SOURCE
111  const FwSizeType affinity = arguments.m_cpuAffinity;
112  cpu_set_t cpu_set;
113  CPU_ZERO(&cpu_set);
114  CPU_SET(static_cast<PlatformIntType>(affinity), &cpu_set);
115 
116  // According to the man-page this function sets errno rather than returning an error status like other functions
117  status = pthread_attr_setaffinity_np(&attributes, sizeof(cpu_set_t), &cpu_set);
118  status = (status == PosixTaskHandle::SUCCESS) ? status : errno;
119 #else
120  Fw::Logger::logMsg("[WARNING] %s setting CPU affinity is only available with GNU pthreads\n",
121  reinterpret_cast<PlatformPointerCastType>(const_cast<CHAR*>(arguments.m_name.toChar())));
122 #endif
123  return status;
124  }
125 
126  Os::Task::Status PosixTask::create(const Os::Task::Arguments& arguments, const PosixTask::PermissionExpectation permissions) {
127  PlatformIntType pthread_status = PosixTaskHandle::SUCCESS;
128  PosixTaskHandle& handle = this->m_handle;
129  const bool expect_permission = (permissions == EXPECT_PERMISSION);
130  // Initialize and clear pthread attributes
131  pthread_attr_t attributes;
132  memset(&attributes, 0, sizeof(attributes));
133  pthread_status = pthread_attr_init(&attributes);
134  if ((arguments.m_stackSize != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
135  pthread_status = set_stack_size(attributes, arguments);
136  }
137  if ((arguments.m_priority != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
138  pthread_status = set_priority_params(attributes, arguments);
139  }
140  if ((arguments.m_cpuAffinity != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
141  pthread_status = set_cpu_affinity(attributes, arguments);
142  }
143  if (pthread_status == PosixTaskHandle::SUCCESS) {
144  pthread_status = pthread_create(&handle.m_task_descriptor, &attributes, pthread_entry_wrapper,
145  arguments.m_routine_argument);
146  }
147  // Successful execution of all precious steps will result in a valid task handle
148  if (pthread_status == PosixTaskHandle::SUCCESS) {
149  handle.m_is_valid = true;
150  }
151 
152  (void) pthread_attr_destroy(&attributes);
153  return Posix::posix_status_to_task_status(pthread_status);
154  }
155 
157 
159  FW_ASSERT(arguments.m_routine != nullptr);
160 
161  // Try to create thread with assuming permissions
162  Os::Task::Status status = this->create(arguments, PermissionExpectation::EXPECT_PERMISSION);
163  // Failure due to permission automatically retried
164  if (status == Os::Task::Status::ERROR_PERMISSION) {
165  if (not PosixTask::s_permissions_reported) {
166  Fw::Logger::logMsg("\n");
167  Fw::Logger::logMsg("[NOTE] Task Permissions:\n");
168  Fw::Logger::logMsg("[NOTE]\n");
169  Fw::Logger::logMsg("[NOTE] You have insufficient permissions to create a task with priority and/or cpu affinity.\n");
170  Fw::Logger::logMsg("[NOTE] A task without priority and affinity will be created.\n");
171  Fw::Logger::logMsg("[NOTE]\n");
172  Fw::Logger::logMsg("[NOTE] There are three possible resolutions:\n");
173  Fw::Logger::logMsg("[NOTE] 1. Use tasks without priority and affinity using parameterless start()\n");
174  Fw::Logger::logMsg("[NOTE] 2. Run this executable as a user with task priority permission\n");
175  Fw::Logger::logMsg("[NOTE] 3. Grant capability with \"setcap 'cap_sys_nice=eip'\" or equivalent\n");
176  Fw::Logger::logMsg("\n");
177  PosixTask::s_permissions_reported = true;
178  }
179  // Fallback with no permission
180  status = this->create(arguments, PermissionExpectation::EXPECT_NO_PERMISSION);
181  } else if (status != Os::Task::Status::OP_OK) {
182  Fw::Logger::logMsg("[ERROR] Failed to create task with status: %d",
183  static_cast<PlatformPointerCastType>(static_cast<PlatformIntType>(status)));
184  }
185  return status;
186  }
187 
189  Os::Task::Status status = Os::Task::Status::JOIN_ERROR;
190  if (not this->m_handle.m_is_valid) {
191  status = Os::Task::Status::INVALID_HANDLE;
192  } else {
193  PlatformIntType stat = ::pthread_join(this->m_handle.m_task_descriptor, nullptr);
194  status = (stat == PosixTaskHandle::SUCCESS) ? Os::Task::Status::OP_OK : Os::Task::Status::JOIN_ERROR;
195  }
196  return status;
197  }
198 
200  return &this->m_handle;
201  }
202 
203  // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
204  // is no top-level pthreads support for suspend and resume.
206  FW_ASSERT(0);
207  }
208 
210  FW_ASSERT(0);
211  }
212 } // end namespace Task
213 } // end namespace Posix
214 } // end namespace Os
#define FW_ASSERT(...)
Definition: Assert.hpp:14
char CHAR
Definition: BasicTypes.h:28
uint8_t PlatformPointerCastType
int PlatformIntType
DefaultTypes.hpp provides fallback defaults for the platform types.
PlatformSizeType FwSizeType
Definition: FpConfig.h:30
#define PRI_FwSizeType
Definition: FpConfig.h:31
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
void suspend(SuspensionType suspensionType) override
suspend the task given the suspension type
Definition: Task.cpp:205
PermissionExpectation
Enumeration of permission expectations.
Definition: Task.hpp:38
@ EXPECT_PERMISSION
Expect that you hold necessary permissions.
Definition: Task.hpp:39
Status join() override
block until the task has ended
Definition: Task.cpp:188
void resume() override
resume a suspended task
Definition: Task.cpp:209
TaskHandle * getHandle() override
return the underlying task handle (implementation specific)
Definition: Task.cpp:199
void onStart() override
perform required task start actions
Definition: Task.cpp:156
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:158
Wrapper for task routine that ensures onStart() is called once the task actually begins.
Definition: Task.hpp:198
static void run(void *task_pointer)
run the task routine wrapper
Definition: Task.cpp:27
Task handle representation.
Definition: Task.hpp:24
const Os::TaskString m_name
Definition: Task.hpp:82
FwSizeType m_cpuAffinity
Definition: Task.hpp:87
static constexpr FwSizeType TASK_DEFAULT
Definition: Task.hpp:28
const char * toChar() const
Definition: TaskString.hpp:45
@ OP_OK
Operation was successful.
Definition: FileSystem.hpp:15
PlatformIntType set_priority_params(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:71
void * pthread_entry_wrapper(void *wrapper_pointer)
Definition: Task.cpp:25
PlatformIntType set_cpu_affinity(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:107
PlatformIntType set_stack_size(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:32
static const PlatformIntType SCHED_POLICY
Definition: Task.cpp:21
Task::Status posix_status_to_task_status(PlatformIntType posix_status)
Definition: error.cpp:51
static constexpr PlatformIntType SUCCESS
Definition: Task.hpp:25
pthread_t m_task_descriptor
Posix task descriptor.
Definition: Task.hpp:28
bool m_is_valid
Is the above descriptor valid.
Definition: Task.hpp:30