F´ Flight Software - C/C++ Documentation
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) {
33  PlatformIntType status = PosixTaskHandle::SUCCESS;
34  FwSizeType stack = arguments.m_stackSize;
35 // Check for stack size multiple of page size or skip when the function
36 // is unavailable.
37 #ifdef _SC_PAGESIZE
38  long page_size = sysconf(_SC_PAGESIZE);
39 #else
40  long page_size = -1; // Force skip and warning
41 #endif
42  if (page_size <= 0) {
44  "[WARNING] %s could not determine page size %s. Skipping stack-size check.\n",
45  const_cast<CHAR*>(arguments.m_name.toChar()),
46  strerror(errno)
47  );
48  }
49  else if ((stack % static_cast<FwSizeType>(page_size)) != 0) {
50  // Round-down to nearest page size multiple
51  FwSizeType rounded = (stack / static_cast<FwSizeType>(page_size)) * static_cast<FwSizeType>(page_size);
53  "[WARNING] %s stack size of %" PRI_FwSizeType " is not multiple of page size %ld, rounding to %" PRI_FwSizeType "\n",
54  const_cast<CHAR*>(arguments.m_name.toChar()),
55  stack,
56  page_size,
57  rounded
58  );
59  stack = rounded;
60  }
61 
62  // Clamp invalid stack sizes
63  if (stack <= static_cast<FwSizeType>(PTHREAD_STACK_MIN)) {
65  "[WARNING] %s stack size of %" PRI_FwSizeType " is too small, clamping to %" PRI_FwSizeType "\n",
66  const_cast<CHAR*>(arguments.m_name.toChar()),
67  stack,
68  static_cast<FwSizeType>(PTHREAD_STACK_MIN)
69  );
70  stack = static_cast<FwSizeType>(PTHREAD_STACK_MIN);
71  }
72  status = pthread_attr_setstacksize(&attributes, static_cast<size_t>(stack));
73  return status;
74  }
75 
76  PlatformIntType set_priority_params(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
77  const FwSizeType min_priority = static_cast<FwSizeType>(sched_get_priority_min(SCHED_POLICY));
78  const FwSizeType max_priority = static_cast<FwSizeType>(sched_get_priority_max(SCHED_POLICY));
79  PlatformIntType status = PosixTaskHandle::SUCCESS;
80  FwSizeType priority = arguments.m_priority;
81  // Clamp to minimum priority
82  if (priority < min_priority) {
83  Fw::Logger::log("[WARNING] %s low task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
84  const_cast<CHAR*>(arguments.m_name.toChar()),
85  priority,
86  min_priority);
87  priority = min_priority;
88  }
89  // Clamp to maximum priority
90  else if (priority > max_priority) {
91  Fw::Logger::log("[WARNING] %s high task priority of %" PRI_FwSizeType " clamped to %" PRI_FwSizeType "\n",
92  const_cast<CHAR*>(arguments.m_name.toChar()),
93  priority,
94  max_priority);
95  priority = max_priority;
96  }
97 
98  // Set attributes required for priority
99  status = pthread_attr_setschedpolicy(&attributes, SCHED_POLICY);
100  if (status == PosixTaskHandle::SUCCESS) {
101  status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
102  }
103  if (status == PosixTaskHandle::SUCCESS) {
104  sched_param schedParam;
105  memset(&schedParam, 0, sizeof(sched_param));
106  schedParam.sched_priority = static_cast<PlatformIntType>(priority);
107  status = pthread_attr_setschedparam(&attributes, &schedParam);
108  }
109  return status;
110  }
111 
112  PlatformIntType set_cpu_affinity(pthread_attr_t& attributes, const Os::Task::Arguments& arguments) {
113  PlatformIntType status = 0;
114 // pthread_attr_setaffinity_np is a non-POSIX function. Notably, it is not available on musl.
115 // Limit its use to builds that involve glibc, on Linux, with _GNU_SOURCE defined.
116 // That's the circumstance in which we expect this feature to work.
117 #if defined(TGT_OS_TYPE_LINUX) && defined(__GLIBC__) && defined(_GNU_SOURCE)
118  const FwSizeType affinity = arguments.m_cpuAffinity;
119  cpu_set_t cpu_set;
120  CPU_ZERO(&cpu_set);
121  CPU_SET(static_cast<PlatformIntType>(affinity), &cpu_set);
122 
123  // According to the man-page this function sets errno rather than returning an error status like other functions
124  status = pthread_attr_setaffinity_np(&attributes, sizeof(cpu_set_t), &cpu_set);
125  status = (status == PosixTaskHandle::SUCCESS) ? status : errno;
126 #else
127  Fw::Logger::log("[WARNING] %s setting CPU affinity is only available with GNU pthreads\n",
128  const_cast<CHAR*>(arguments.m_name.toChar()));
129 #endif
130  return status;
131  }
132 
133  Os::Task::Status PosixTask::create(const Os::Task::Arguments& arguments, const PosixTask::PermissionExpectation permissions) {
134  PlatformIntType pthread_status = PosixTaskHandle::SUCCESS;
135  PosixTaskHandle& handle = this->m_handle;
136  const bool expect_permission = (permissions == EXPECT_PERMISSION);
137  // Initialize and clear pthread attributes
138  pthread_attr_t attributes;
139  memset(&attributes, 0, sizeof(attributes));
140  pthread_status = pthread_attr_init(&attributes);
141  if ((arguments.m_stackSize != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
142  pthread_status = set_stack_size(attributes, arguments);
143  }
144  if ((arguments.m_priority != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
145  pthread_status = set_priority_params(attributes, arguments);
146  }
147  if ((arguments.m_cpuAffinity != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
148  pthread_status = set_cpu_affinity(attributes, arguments);
149  }
150  if (pthread_status == PosixTaskHandle::SUCCESS) {
151  pthread_status = pthread_create(&handle.m_task_descriptor, &attributes, pthread_entry_wrapper,
152  arguments.m_routine_argument);
153  }
154  // Successful execution of all precious steps will result in a valid task handle
155  if (pthread_status == PosixTaskHandle::SUCCESS) {
156  handle.m_is_valid = true;
157  }
158 
159  (void) pthread_attr_destroy(&attributes);
160  return Posix::posix_status_to_task_status(pthread_status);
161  }
162 
164 
166  FW_ASSERT(arguments.m_routine != nullptr);
167 
168  // Try to create thread with assuming permissions
169  Os::Task::Status status = this->create(arguments, PermissionExpectation::EXPECT_PERMISSION);
170  // Failure due to permission automatically retried
171  if (status == Os::Task::Status::ERROR_PERMISSION) {
172  if (not PosixTask::s_permissions_reported) {
173  Fw::Logger::log("\n");
174  Fw::Logger::log("[NOTE] Task Permissions:\n");
175  Fw::Logger::log("[NOTE]\n");
176  Fw::Logger::log("[NOTE] You have insufficient permissions to create a task with priority and/or cpu affinity.\n");
177  Fw::Logger::log("[NOTE] A task without priority and affinity will be created.\n");
178  Fw::Logger::log("[NOTE]\n");
179  Fw::Logger::log("[NOTE] There are three possible resolutions:\n");
180  Fw::Logger::log("[NOTE] 1. Use tasks without priority and affinity using parameterless start()\n");
181  Fw::Logger::log("[NOTE] 2. Run this executable as a user with task priority permission\n");
182  Fw::Logger::log("[NOTE] 3. Grant capability with \"setcap 'cap_sys_nice=eip'\" or equivalent\n");
183  Fw::Logger::log("\n");
184  PosixTask::s_permissions_reported = true;
185  }
186  // Fallback with no permission
187  status = this->create(arguments, PermissionExpectation::EXPECT_NO_PERMISSION);
188  } else if (status != Os::Task::Status::OP_OK) {
189  Fw::Logger::log("[ERROR] Failed to create task with status: %d",
190  static_cast<PlatformIntType>(status));
191  }
192  return status;
193  }
194 
196  Os::Task::Status status = Os::Task::Status::JOIN_ERROR;
197  if (not this->m_handle.m_is_valid) {
198  status = Os::Task::Status::INVALID_HANDLE;
199  } else {
200  PlatformIntType stat = ::pthread_join(this->m_handle.m_task_descriptor, nullptr);
201  status = (stat == PosixTaskHandle::SUCCESS) ? Os::Task::Status::OP_OK : Os::Task::Status::JOIN_ERROR;
202  }
203  return status;
204  }
205 
207  return &this->m_handle;
208  }
209 
210  // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
211  // is no top-level pthreads support for suspend and resume.
213  FW_ASSERT(0);
214  }
215 
217  FW_ASSERT(0);
218  }
219 
220 
222  Os::Task::Status task_status = Os::Task::OP_OK;
223  timespec sleep_interval;
224  sleep_interval.tv_sec = interval.getSeconds();
225  sleep_interval.tv_nsec = interval.getUSeconds() * 1000;
226 
227  timespec remaining_interval;
228  remaining_interval.tv_sec = 0;
229  remaining_interval.tv_nsec = 0;
230 
231  while (true) {
232  PlatformIntType status = nanosleep(&sleep_interval, &remaining_interval);
233  // Success, return ok
234  if (0 == status) {
235  break;
236  }
237  // Interrupted, reset sleep and iterate
238  else if (EINTR == errno) {
239  sleep_interval = remaining_interval;
240  continue;
241  }
242  // Anything else is an error
243  else {
244  task_status = Os::Task::Status::DELAY_ERROR;
245  break;
246  }
247  }
248  return task_status;
249  }
250 
251 
252 } // end namespace Task
253 } // end namespace Posix
254 } // end namespace Os
static constexpr FwSizeType TASK_DEFAULT
Definition: Task.hpp:28
Task handle representation.
Definition: Task.hpp:24
Operation succeeded.
Definition: Os.hpp:26
PlatformSizeType FwSizeType
static constexpr PlatformIntType SUCCESS
Definition: Task.hpp:25
const char * toChar() const
Definition: TaskString.hpp:45
#define PRI_FwSizeType
PlatformIntType set_cpu_affinity(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:112
void suspend(SuspensionType suspensionType) override
suspend the task given the suspension type
Definition: Task.cpp:212
static void log(const char *format,...)
log a formated string with supplied arguments
Definition: Logger.cpp:21
Status _delay(Fw::TimeInterval interval) override
delay the current task
Definition: Task.cpp:221
PlatformIntType set_priority_params(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:76
static const PlatformIntType SCHED_POLICY
Definition: Task.cpp:21
Task::Status posix_status_to_task_status(PlatformIntType posix_status)
Definition: error.cpp:147
message sent/received okay
Definition: Task.hpp:30
bool m_is_valid
Is the above descriptor valid.
Definition: Task.hpp:30
PermissionExpectation
Enumeration of permission expectations.
Definition: Task.hpp:38
pthread_t m_task_descriptor
Posix task descriptor.
Definition: Task.hpp:28
TaskHandle * getHandle() override
return the underlying task handle (implementation specific)
Definition: Task.cpp:206
static void run(void *task_pointer)
run the task routine wrapper
Definition: Task.cpp:27
Wrapper for task routine that ensures onStart() is called once the task actually begins.
Definition: Task.hpp:200
const Os::TaskString m_name
Definition: Task.hpp:84
void * pthread_entry_wrapper(void *wrapper_pointer)
Definition: Task.cpp:25
FwSizeType m_cpuAffinity
Definition: Task.hpp:89
U32 getUSeconds() const
Expect that you hold necessary permissions.
Definition: Task.hpp:39
Status join() override
block until the task has ended
Definition: Task.cpp:195
PlatformIntType set_stack_size(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition: Task.cpp:32
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:165
void resume() override
resume a suspended task
Definition: Task.cpp:216
void onStart() override
perform required task start actions
Definition: Task.cpp:163
#define FW_ASSERT(...)
Definition: Assert.hpp:14
U32 getSeconds() const