Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add realtime priority inheritance mutexes #197

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c2e1c10
add first version of the mutex with priority inheritance
saikishor Nov 13, 2024
05ce6bc
added documentation and remove the copy constructor and assignment op…
saikishor Nov 14, 2024
1921f21
change the name of the mutex to priority_inheritance_mutex
saikishor Nov 19, 2024
1a18c6e
Remove pthread_mutexattr_setprioceiling as the protocol is set to PTH…
saikishor Nov 19, 2024
040a904
templatize the class and create different instance with using
saikishor Nov 19, 2024
742177e
Add cerr to mention that the owner is died so user is aware of it
saikishor Nov 19, 2024
f392b21
Add tests to the priority inheritance mutex
saikishor Nov 19, 2024
bad11ed
add documentation and more template arguments
saikishor Nov 20, 2024
9f33a30
Address MR comments
saikishor Nov 20, 2024
0a8343f
remove the virtual instances from the methods
saikishor Nov 21, 2024
bb1f880
Change the exceptions to system_error
saikishor Nov 21, 2024
1b371cc
Change to cerrno
saikishor Nov 21, 2024
1d0f911
Add noexcept to some methods
saikishor Nov 21, 2024
8ecf332
use attr_cleanup_t to cleanup properly without any memory_leaks
saikishor Nov 21, 2024
bbfb3ca
Remove the protocol and ceiling template arguments
saikishor Nov 21, 2024
3e610d9
Add detail namespace as suggested
saikishor Nov 21, 2024
0fab814
test with different lock constructors
saikishor Nov 22, 2024
cee4ee9
Change to system_categeory
saikishor Nov 22, 2024
0611b82
add tests on deadlock detection
saikishor Nov 22, 2024
07e4e3a
Just use the default mutex with PTHREAD_MUTEX_ERRORCHECK
saikishor Nov 22, 2024
91cce17
Rename the mutexes prefixing `prio_inherit_`
saikishor Nov 23, 2024
8c6e1e2
Merge branch 'master' into add/realtime/mutexes
saikishor Nov 26, 2024
6f332bf
Exclude the windows using mutex.hpp
saikishor Nov 27, 2024
c16d73a
Merge branch 'master' into add/realtime/mutexes
saikishor Nov 27, 2024
58452ff
create class enums for defining the template types
saikishor Nov 25, 2024
5b3028c
Make the mutexes work with the std::is_same for conditioning in code
saikishor Nov 27, 2024
7409bca
Merge branch 'master' into add/realtime/mutexes
saikishor Nov 28, 2024
3a7edb2
indent the condition in CMakeLists.txt
saikishor Dec 3, 2024
3dd885f
Merge branch 'master' into add/realtime/mutexes
saikishor Dec 3, 2024
ca86ba3
Merge branch 'master' into add/realtime/mutexes
saikishor Dec 9, 2024
f7198ee
Merge branch 'master' into add/realtime/mutexes
saikishor Dec 15, 2024
8ee40da
Merge branch 'master' into add/realtime/mutexes
saikishor Dec 18, 2024
8b0bded
Merge branch 'master' into add/realtime/mutexes
saikishor Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ if(BUILD_TESTING)
ament_add_gmock(test_async_function_handler test/test_async_function_handler.cpp)
target_link_libraries(test_async_function_handler realtime_tools thread_priority)
ament_target_dependencies(test_async_function_handler lifecycle_msgs rclcpp_lifecycle)

ament_add_gmock(realtime_mutex_tests test/realtime_mutex_tests.cpp)
target_link_libraries(realtime_mutex_tests realtime_tools)
endif()

# Install
Expand Down
175 changes: 175 additions & 0 deletions include/realtime_tools/mutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright 2024 PAL Robotics S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// \author Sai Kishor Kothakota

#ifndef REALTIME_TOOLS__MUTEX_HPP_
#define REALTIME_TOOLS__MUTEX_HPP_

#include <pthread.h>
#include <cerrno>
#include <cstring>
christophfroehlich marked this conversation as resolved.
Show resolved Hide resolved
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>

/**
* @brief A pthread mutex wrapper that provides a mutex with the priority inheritance
* protocol and a priority ceiling of 99.
* The mutex is also error checked and robust.
* This mutex is intended to be used in real-time contexts.
* @note This mutex is not recursive.
*/
namespace realtime_tools
{
namespace detail
{
/**
* @brief A class template that provides a pthread mutex with the priority inheritance protocol
*
* @tparam MutexType The type of the mutex. It can be one of the following: PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_DEFAULT
saikishor marked this conversation as resolved.
Show resolved Hide resolved
* @tparam MutexRobustness The robustness of the mutex. It can be one of the following: PTHREAD_MUTEX_STALLED, PTHREAD_MUTEX_ROBUST
*/
template <int MutexType, int MutexRobustness>
saikishor marked this conversation as resolved.
Show resolved Hide resolved
saikishor marked this conversation as resolved.
Show resolved Hide resolved
class mutex
{
public:
using native_handle_type = pthread_mutex_t *;

mutex()
{
pthread_mutexattr_t attr;

const auto attr_destroy = [](pthread_mutexattr_t * mutex_attr) {
// Destroy the mutex attributes
const auto res_destroy = pthread_mutexattr_destroy(mutex_attr);
if (res_destroy != 0) {
throw std::system_error(
res_destroy, std::generic_category(), "Failed to destroy mutex attribute");
}
};
using attr_cleanup_t = std::unique_ptr<pthread_mutexattr_t, decltype(attr_destroy)>;
auto attr_cleanup = attr_cleanup_t(&attr, attr_destroy);

// Initialize the mutex attributes
const auto res_attr = pthread_mutexattr_init(&attr);
if (res_attr != 0) {
throw std::system_error(
res_attr, std::system_category(), "Failed to initialize mutex attribute");
}

// Set the mutex type to MutexType
saikishor marked this conversation as resolved.
Show resolved Hide resolved
const auto res_type = pthread_mutexattr_settype(&attr, MutexType);

if (res_type != 0) {
throw std::system_error(res_type, std::system_category(), "Failed to set mutex type");
}

// Set the mutex attribute to use the protocol PTHREAD_PRIO_INHERIT
const auto res_protocol = pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
if (res_protocol != 0) {
throw std::system_error(res_protocol, std::system_category(), "Failed to set mutex protocol");
}

// Set the mutex attribute robustness to MutexRobustness
const auto res_robust = pthread_mutexattr_setrobust(&attr, MutexRobustness);
if (res_robust != 0) {
throw std::system_error(res_robust, std::system_category(), "Failed to set mutex robustness");
}

// Initialize the mutex with the attributes
const auto res_init = pthread_mutex_init(&mutex_, &attr);
if (res_init != 0) {
throw std::system_error(res_init, std::system_category(), "Failed to initialize mutex");
}
}

~mutex()
{
const auto res = pthread_mutex_destroy(&mutex_);
if (res != 0) {
std::cerr << "Failed to destroy mutex : " << std::strerror(res) << std::endl;
saikishor marked this conversation as resolved.
Show resolved Hide resolved
}
}

mutex(const mutex &) = delete;

mutex & operator=(const mutex &) = delete;

native_handle_type native_handle() noexcept { return &mutex_; }

void lock()
{
const auto res = pthread_mutex_lock(&mutex_);
if (res == 0) {
return;
}
if (res == EOWNERDEAD) {
const auto res_consistent = pthread_mutex_consistent(&mutex_);
if (res_consistent != 0) {
throw std::runtime_error(
std::string("Failed to make mutex consistent : ") + std::strerror(res_consistent));
}
std::cerr << "Mutex owner died, but the mutex is consistent now. This shouldn't happen!"
<< std::endl;
} else if (res == EDEADLK) {
throw std::system_error(res, std::system_category(), "Deadlock detected");
} else {
saikishor marked this conversation as resolved.
Show resolved Hide resolved
throw std::runtime_error(std::string("Failed to lock mutex : ") + std::strerror(res));
}
}

void unlock() noexcept
{
// As per the requirements of BasicLockable concept, unlock should not throw
const auto res = pthread_mutex_unlock(&mutex_);
if (res != 0) {
std::cerr << "Failed to unlock mutex : " << std::strerror(res) << std::endl;
}
}

bool try_lock()
{
const auto res = pthread_mutex_trylock(&mutex_);
if (res == 0) {
return true;
}
if (res == EBUSY) {
return false;
} else if (res == EOWNERDEAD) {
const auto res_consistent = pthread_mutex_consistent(&mutex_);
if (res_consistent != 0) {
throw std::runtime_error(
std::string("Failed to make mutex consistent : ") + std::strerror(res_consistent));
}
std::cerr << "Mutex owner died, but the mutex is consistent now. This shouldn't happen!"
<< std::endl;
} else if (res == EDEADLK) {
throw std::system_error(res, std::system_category(), "Deadlock detected");
} else {
throw std::runtime_error(std::string("Failed to try lock mutex : ") + std::strerror(res));
saikishor marked this conversation as resolved.
Show resolved Hide resolved
}
return true;
}

private:
pthread_mutex_t mutex_;
};
} // namespace detail
using prio_inherit_mutex = detail::mutex<PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_ROBUST>;
using prio_inherit_recursive_mutex = detail::mutex<PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ROBUST>;
} // namespace realtime_tools

#endif // REALTIME_TOOLS__MUTEX_HPP_
Loading
Loading