ros2_control - jazzy
Loading...
Searching...
No Matches
mutex.hpp
1// Copyright 2024 PAL Robotics S.L.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
16
17#ifndef REALTIME_TOOLS__MUTEX_HPP_
18#define REALTIME_TOOLS__MUTEX_HPP_
19
20#ifdef _WIN32
21#error "The mutex.hpp header is not supported on Windows platforms"
22#endif
23
24#include <pthread.h>
25#include <cerrno>
26#include <cstring>
27#include <iostream>
28#include <memory>
29#include <stdexcept>
30#include <string>
31
39namespace realtime_tools
40{
41namespace detail
42{
44{
45 static constexpr int value = PTHREAD_MUTEX_ERRORCHECK;
46};
47
49{
50 static constexpr int value = PTHREAD_MUTEX_RECURSIVE;
51};
52
54{
55#if defined(__linux__)
56 static constexpr int value = PTHREAD_MUTEX_STALLED;
57#else
58 static constexpr int value = 0; // macOS, Windows, or other platforms fallback
59#endif
60};
61
63{
64#if defined(__linux__)
65 static constexpr int value = PTHREAD_MUTEX_ROBUST;
66#else
67 static constexpr int value = 0; // macOS, Windows, or other platforms fallback
68#endif
69};
76template <typename MutexType, typename MutexRobustness>
77class mutex
78{
79public:
80 using native_handle_type = pthread_mutex_t *;
81 using type = MutexType;
82 using robustness = MutexRobustness;
83
84 mutex()
85 {
86 pthread_mutexattr_t attr;
87
88 const auto attr_destroy = [](pthread_mutexattr_t * mutex_attr) {
89 // Destroy the mutex attributes
90 const auto res_destroy = pthread_mutexattr_destroy(mutex_attr);
91 if (res_destroy != 0) {
92 throw std::system_error(
93 res_destroy, std::generic_category(), "Failed to destroy mutex attribute");
94 }
95 };
96 using attr_cleanup_t = std::unique_ptr<pthread_mutexattr_t, decltype(attr_destroy)>;
97 auto attr_cleanup = attr_cleanup_t(&attr, attr_destroy);
98
99 // Initialize the mutex attributes
100 const auto res_attr = pthread_mutexattr_init(&attr);
101 if (res_attr != 0) {
102 throw std::system_error(
103 res_attr, std::system_category(), "Failed to initialize mutex attribute");
104 }
105
106 // Set the mutex type to MutexType
107 const auto res_type = pthread_mutexattr_settype(&attr, MutexType::value);
108
109 if (res_type != 0) {
110 throw std::system_error(res_type, std::system_category(), "Failed to set mutex type");
111 }
112
113 // Set the mutex attribute to use the protocol PTHREAD_PRIO_INHERIT
114 const auto res_protocol = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
115 if (res_protocol != 0) {
116 throw std::system_error(res_protocol, std::system_category(), "Failed to set mutex protocol");
117 }
118
119 // Set the mutex attribute robustness to MutexRobustness
120 // On platforms like macOS, pthread_mutexattr_setrobust is not available,
121 // so skip this step
122#if defined(__linux__)
123 const auto res_robust = pthread_mutexattr_setrobust(&attr, MutexRobustness::value);
124 if (res_robust != 0) {
125 throw std::system_error(res_robust, std::system_category(), "Failed to set mutex robustness");
126 }
127#endif
128
129 // Initialize the mutex with the attributes
130 const auto res_init = pthread_mutex_init(&mutex_, &attr);
131 if (res_init != 0) {
132 throw std::system_error(res_init, std::system_category(), "Failed to initialize mutex");
133 }
134 }
135
136 ~mutex()
137 {
138 const auto res = pthread_mutex_destroy(&mutex_);
139 if (res != 0) {
140 std::cerr << "Failed to destroy mutex : " << std::strerror(res) << std::endl;
141 }
142 }
143
144 mutex(const mutex &) = delete;
145
146 mutex & operator=(const mutex &) = delete;
147
148 native_handle_type native_handle() noexcept { return &mutex_; }
149
150 void lock()
151 {
152 const auto res = pthread_mutex_lock(&mutex_);
153 if (res == 0) {
154 return;
155 }
156 if (res == EOWNERDEAD) {
157#if defined(__linux__)
158 const auto res_consistent = pthread_mutex_consistent(&mutex_);
159 if (res_consistent != 0) {
160 throw std::runtime_error(
161 std::string("Failed to make mutex consistent : ") + std::strerror(res_consistent));
162 }
163 std::cerr << "Mutex owner died, but the mutex is consistent now. This shouldn't happen!"
164 << std::endl;
165#else
166 // On platforms without pthread_mutex_consistent support, just log a warning
167 std::cerr
168 << "Mutex owner died, but pthread_mutex_consistent is not supported on this platform."
169 << std::endl;
170#endif
171 } else if (res == EDEADLK) {
172 throw std::system_error(res, std::system_category(), "Deadlock detected");
173 } else {
174 throw std::runtime_error(std::string("Failed to lock mutex : ") + std::strerror(res));
175 }
176 }
177
178 void unlock() noexcept
179 {
180 // As per the requirements of BasicLockable concept, unlock should not throw
181 const auto res = pthread_mutex_unlock(&mutex_);
182 if (res != 0) {
183 std::cerr << "Failed to unlock mutex : " << std::strerror(res) << std::endl;
184 }
185 }
186
187 bool try_lock()
188 {
189 const auto res = pthread_mutex_trylock(&mutex_);
190 if (res == 0) {
191 return true;
192 }
193 if (res == EBUSY) {
194 return false;
195 } else if (res == EOWNERDEAD) {
196#if defined(__linux__)
197 const auto res_consistent = pthread_mutex_consistent(&mutex_);
198 if (res_consistent != 0) {
199 throw std::runtime_error(
200 std::string("Failed to make mutex consistent : ") + std::strerror(res_consistent));
201 }
202 std::cerr << "Mutex owner died, but the mutex is consistent now. This shouldn't happen!"
203 << std::endl;
204#else
205 std::cerr
206 << "Mutex owner died, but pthread_mutex_consistent is not supported on this platform."
207 << std::endl;
208#endif
209 } else if (res == EDEADLK) {
210 throw std::system_error(res, std::system_category(), "Deadlock detected");
211 } else {
212 throw std::runtime_error(std::string("Failed to try lock mutex : ") + std::strerror(res));
213 }
214 return true;
215 }
216
217private:
218 pthread_mutex_t mutex_;
219};
220} // namespace detail
224} // namespace realtime_tools
225
226#endif // REALTIME_TOOLS__MUTEX_HPP_
A class template that provides a pthread mutex with the priority inheritance protocol.
Definition mutex.hpp:78
A pthread mutex wrapper that provides a mutex with the priority inheritance protocol and a priority c...
Definition async_function_handler.hpp:38