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
2327using 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, ¶ms) == 0 );
159+ success &= (calculate_os_fifo_thread_priority (priority, ¶ms.sched_priority ) ==
160+ RCUTILS_RET_OK ? 1 : 0 );
161+ success &= (pthread_setschedparam (native_handle, SCHED_FIFO, ¶ms) == 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+
98185template <class MutexClass >
99186void 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) {
197284TEST (test_mutex, rpimutex_priority_inversion) {
198285 priority_inheritance_test<rcpputils::RecursivePIMutex>();
199286}
287+
288+ #endif // __linux__
0 commit comments