F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
Loading...
Searching...
No Matches
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
17namespace Os {
18namespace Posix {
19namespace 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 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));
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// Feature set check for _GNU_SOURCE before using GNU only features
115#ifdef _GNU_SOURCE
116 const FwSizeType affinity = arguments.m_cpuAffinity;
117 cpu_set_t cpu_set;
118 CPU_ZERO(&cpu_set);
119 CPU_SET(static_cast<PlatformIntType>(affinity), &cpu_set);
120
121 // According to the man-page this function sets errno rather than returning an error status like other functions
122 status = pthread_attr_setaffinity_np(&attributes, sizeof(cpu_set_t), &cpu_set);
123 status = (status == PosixTaskHandle::SUCCESS) ? status : errno;
124#else
125 Fw::Logger::log("[WARNING] %s setting CPU affinity is only available with GNU pthreads\n",
126 const_cast<CHAR*>(arguments.m_name.toChar()));
127#endif
128 return status;
129 }
130
131 Os::Task::Status PosixTask::create(const Os::Task::Arguments& arguments, const PosixTask::PermissionExpectation permissions) {
133 PosixTaskHandle& handle = this->m_handle;
134 const bool expect_permission = (permissions == EXPECT_PERMISSION);
135 // Initialize and clear pthread attributes
136 pthread_attr_t attributes;
137 memset(&attributes, 0, sizeof(attributes));
138 pthread_status = pthread_attr_init(&attributes);
139 if ((arguments.m_stackSize != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
140 pthread_status = set_stack_size(attributes, arguments);
141 }
142 if ((arguments.m_priority != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
143 pthread_status = set_priority_params(attributes, arguments);
144 }
145 if ((arguments.m_cpuAffinity != Os::Task::TASK_DEFAULT) && (expect_permission) && (pthread_status == PosixTaskHandle::SUCCESS)) {
146 pthread_status = set_cpu_affinity(attributes, arguments);
147 }
148 if (pthread_status == PosixTaskHandle::SUCCESS) {
149 pthread_status = pthread_create(&handle.m_task_descriptor, &attributes, pthread_entry_wrapper,
150 arguments.m_routine_argument);
151 }
152 // Successful execution of all precious steps will result in a valid task handle
153 if (pthread_status == PosixTaskHandle::SUCCESS) {
154 handle.m_is_valid = true;
155 }
156
157 (void) pthread_attr_destroy(&attributes);
158 return Posix::posix_status_to_task_status(pthread_status);
159 }
160
162
164 FW_ASSERT(arguments.m_routine != nullptr);
165
166 // Try to create thread with assuming permissions
167 Os::Task::Status status = this->create(arguments, PermissionExpectation::EXPECT_PERMISSION);
168 // Failure due to permission automatically retried
170 if (not PosixTask::s_permissions_reported) {
171 Fw::Logger::log("\n");
172 Fw::Logger::log("[NOTE] Task Permissions:\n");
173 Fw::Logger::log("[NOTE]\n");
174 Fw::Logger::log("[NOTE] You have insufficient permissions to create a task with priority and/or cpu affinity.\n");
175 Fw::Logger::log("[NOTE] A task without priority and affinity will be created.\n");
176 Fw::Logger::log("[NOTE]\n");
177 Fw::Logger::log("[NOTE] There are three possible resolutions:\n");
178 Fw::Logger::log("[NOTE] 1. Use tasks without priority and affinity using parameterless start()\n");
179 Fw::Logger::log("[NOTE] 2. Run this executable as a user with task priority permission\n");
180 Fw::Logger::log("[NOTE] 3. Grant capability with \"setcap 'cap_sys_nice=eip'\" or equivalent\n");
181 Fw::Logger::log("\n");
182 PosixTask::s_permissions_reported = true;
183 }
184 // Fallback with no permission
185 status = this->create(arguments, PermissionExpectation::EXPECT_NO_PERMISSION);
186 } else if (status != Os::Task::Status::OP_OK) {
187 Fw::Logger::log("[ERROR] Failed to create task with status: %d",
188 static_cast<PlatformIntType>(status));
189 }
190 return status;
191 }
192
195 if (not this->m_handle.m_is_valid) {
197 } else {
198 PlatformIntType stat = ::pthread_join(this->m_handle.m_task_descriptor, nullptr);
200 }
201 return status;
202 }
203
205 return &this->m_handle;
206 }
207
208 // Note: not implemented for Posix threads. Must be manually done using a mutex or other blocking construct as there
209 // is no top-level pthreads support for suspend and resume.
211 FW_ASSERT(0);
212 }
213
215 FW_ASSERT(0);
216 }
217
218
220 Os::Task::Status task_status = Os::Task::OP_OK;
221 timespec sleep_interval;
222 sleep_interval.tv_sec = interval.getSeconds();
223 sleep_interval.tv_nsec = interval.getUSeconds() * 1000;
224
225 timespec remaining_interval;
226 remaining_interval.tv_sec = 0;
227 remaining_interval.tv_nsec = 0;
228
229 while (true) {
230 PlatformIntType status = nanosleep(&sleep_interval, &remaining_interval);
231 // Success, return ok
232 if (0 == status) {
233 break;
234 }
235 // Interrupted, reset sleep and iterate
236 else if (EINTR == errno) {
237 sleep_interval = remaining_interval;
238 continue;
239 }
240 // Anything else is an error
241 else {
242 task_status = Os::Task::Status::DELAY_ERROR;
243 break;
244 }
245 }
246 return task_status;
247 }
248
249
250} // end namespace Task
251} // end namespace Posix
252} // end namespace Os
#define FW_ASSERT(...)
Definition Assert.hpp:14
char CHAR
Definition BasicTypes.h:32
int PlatformIntType
DefaultTypes.hpp provides fallback defaults for the platform types.
PlatformSizeType FwSizeType
Definition FpConfig.h:35
#define PRI_FwSizeType
Definition FpConfig.h:36
static void log(const char *format,...)
log a formated string with supplied arguments
Definition Logger.cpp:21
U32 getSeconds() const
U32 getUSeconds() const
void suspend(SuspensionType suspensionType) override
suspend the task given the suspension type
Definition Task.cpp:210
PermissionExpectation
Enumeration of permission expectations.
Definition Task.hpp:38
@ EXPECT_NO_PERMISSION
Expect that you do not hold necessary permissions.
Definition Task.hpp:40
@ EXPECT_PERMISSION
Expect that you hold necessary permissions.
Definition Task.hpp:39
Status _delay(Fw::TimeInterval interval) override
delay the current task
Definition Task.cpp:219
Status join() override
block until the task has ended
Definition Task.cpp:193
void resume() override
resume a suspended task
Definition Task.cpp:214
TaskHandle * getHandle() override
return the underlying task handle (implementation specific)
Definition Task.cpp:204
void onStart() override
perform required task start actions
Definition Task.cpp:161
Status start(const Arguments &arguments) override
start the task
Definition Task.cpp:163
Wrapper for task routine that ensures onStart() is called once the task actually begins.
Definition Task.hpp:199
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:83
static constexpr FwSizeType TASK_DEFAULT
Definition Task.hpp:28
@ OP_OK
message sent/received okay
Definition Task.hpp:30
@ ERROR_PERMISSION
permissions error setting-up tasks
Definition Task.hpp:39
@ DELAY_ERROR
error trying to delay the task
Definition Task.hpp:36
@ INVALID_HANDLE
Task handle invalid.
Definition Task.hpp:31
@ JOIN_ERROR
error trying to join the task
Definition Task.hpp:37
const char * toChar() const
void * pthread_entry_wrapper(void *wrapper_pointer)
Definition Task.cpp:25
PlatformIntType set_priority_params(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition Task.cpp:76
PlatformIntType set_cpu_affinity(pthread_attr_t &attributes, const Os::Task::Arguments &arguments)
Definition Task.cpp:112
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:147
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