Skip to content

Commit 4e81d58

Browse files
committed
Thread configuration now part of the test implementation
Signed-off-by: Martin Mayer <[email protected]>
1 parent d455916 commit 4e81d58

File tree

2 files changed

+93
-55
lines changed

2 files changed

+93
-55
lines changed

include/rcpputils/thread.hpp

Lines changed: 0 additions & 51 deletions
This file was deleted.

test/test_mutex.cpp

Lines changed: 93 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
1818
#include <chrono>
1919

2020
#include "rcpputils/mutex.hpp"
21-
#include "rcpputils/thread.hpp"
21+
22+
#ifdef __linux__
23+
#include <pthread.h>
24+
#include "rcutils/types/rcutils_ret.h"
25+
#endif // __linux__
2226

2327
using namespace std::chrono_literals;
2428

@@ -95,6 +99,89 @@ TEST(test_mutex, pimutex_lockthread) {
9599
test_thread.join();
96100
}
97101

102+
#ifdef __linux__
103+
//
104+
// The test cases pimutex_priority_inversion & rpimutex_priority_inversion provoke
105+
// a thread priority inversion. To do so they need to configure the cpu priority,
106+
// scheduler type and cpu core affinity for a thread. Since this functionality is
107+
// not part of ROS2 yet the necessary functionality is implemented here for the
108+
// purpose of implementing these tests. Moreover, the required functionality is
109+
// OS specific and the current implementation is for RT Linux only. Feel free
110+
// to extend the implementation for other realtime capable operating systems,
111+
// like VxWorks and QNX.
112+
//
113+
// Note: for RT Linux elevated user rights are needed for the process.
114+
//
115+
116+
/// Enum for OS independent thread priorities
117+
enum ThreadPriority
118+
{
119+
THREAD_PRIORITY_LOWEST,
120+
THREAD_PRIORITY_LOW,
121+
THREAD_PRIORITY_MEDIUM,
122+
THREAD_PRIORITY_HIGH,
123+
THREAD_PRIORITY_HIGHEST
124+
};
125+
126+
rcutils_ret_t calculate_os_fifo_thread_priority(
127+
const int thread_priority,
128+
int * os_priority)
129+
{
130+
if (thread_priority > THREAD_PRIORITY_HIGHEST || thread_priority < THREAD_PRIORITY_LOWEST) {
131+
return RCUTILS_RET_ERROR;
132+
}
133+
const int max_prio = sched_get_priority_max(SCHED_FIFO);
134+
const int min_prio = sched_get_priority_min(SCHED_FIFO);
135+
const int range_prio = max_prio - min_prio;
136+
137+
int priority = min_prio + (thread_priority - THREAD_PRIORITY_LOWEST) *
138+
range_prio / (THREAD_PRIORITY_HIGHEST - THREAD_PRIORITY_LOWEST);
139+
if (priority > min_prio && priority < max_prio) {
140+
// on Linux systems THREAD_PRIORITY_MEDIUM should be prio 49 instead of 50
141+
// in order to not block any interrupt handlers
142+
priority--;
143+
}
144+
145+
*os_priority = priority;
146+
147+
return RCUTILS_RET_OK;
148+
}
149+
150+
rcutils_ret_t configure_native_realtime_thread(
151+
unsigned long int native_handle, const int priority, // NOLINT
152+
const unsigned int cpu_bitmask)
153+
{
154+
int success = 1;
155+
156+
struct sched_param params;
157+
int policy;
158+
success &= (pthread_getschedparam(native_handle, &policy, &params) == 0);
159+
success &= (calculate_os_fifo_thread_priority(priority, &params.sched_priority) ==
160+
RCUTILS_RET_OK ? 1 : 0);
161+
success &= (pthread_setschedparam(native_handle, SCHED_FIFO, &params) == 0);
162+
163+
cpu_set_t cpuset;
164+
CPU_ZERO(&cpuset);
165+
for (unsigned int i = 0; i < sizeof(cpu_bitmask) * 8; i++) {
166+
if ( (cpu_bitmask & (1 << i)) != 0) {
167+
CPU_SET(i, &cpuset);
168+
}
169+
}
170+
success &= (pthread_setaffinity_np(native_handle, sizeof(cpu_set_t), &cpuset) == 0);
171+
172+
return success ? RCUTILS_RET_OK : RCUTILS_RET_ERROR;
173+
}
174+
175+
bool configure_realtime_thread(
176+
std::thread & thread, const ThreadPriority priority,
177+
const unsigned int cpu_bitmask = (unsigned) -1)
178+
{
179+
rcutils_ret_t return_value = configure_native_realtime_thread(
180+
thread.native_handle(),
181+
priority, cpu_bitmask);
182+
return return_value == RCUTILS_RET_OK ? true : false;
183+
}
184+
98185
template<class MutexClass>
99186
void priority_inheritance_test()
100187
{
@@ -114,7 +201,7 @@ void priority_inheritance_test()
114201
test_mutex.unlock();
115202
std::cout << "Low prio thread unlocked and ends.\n" << std::flush;
116203
});
117-
if (rcpputils::configure_realtime_thread(
204+
if (configure_realtime_thread(
118205
low_prio_thread, THREAD_PRIORITY_LOW,
119206
cpu_bitmask) == false)
120207
{
@@ -141,7 +228,7 @@ void priority_inheritance_test()
141228
std::cout << "High prio thread unlocked and ends.\n" << std::flush;
142229
});
143230
EXPECT_TRUE(
144-
rcpputils::configure_realtime_thread(
231+
configure_realtime_thread(
145232
high_prio_thread,
146233
THREAD_PRIORITY_HIGH, cpu_bitmask)) << "THREAD_PRIORITY_HIGH could not be set.";
147234

@@ -156,7 +243,7 @@ void priority_inheritance_test()
156243
std::cout << "Medium prio thread ends.\n" << std::flush;
157244
});
158245
EXPECT_TRUE(
159-
rcpputils::configure_realtime_thread(
246+
configure_realtime_thread(
160247
medium_prio_thread,
161248
THREAD_PRIORITY_MEDIUM,
162249
cpu_bitmask)) << "THREAD_PRIORITY_MEDIUM could not be set.";
@@ -197,3 +284,5 @@ TEST(test_mutex, pimutex_priority_inversion) {
197284
TEST(test_mutex, rpimutex_priority_inversion) {
198285
priority_inheritance_test<rcpputils::RecursivePIMutex>();
199286
}
287+
288+
#endif // __linux__

0 commit comments

Comments
 (0)