use signal safe synchronization with platform specific semaphores (#607)
* use signal safe synchronization with platform specific semaphores Signed-off-by: William Woodall <william@osrfoundation.org> * addressed feedback and refactored into separate files Signed-off-by: William Woodall <william@osrfoundation.org> * Apply suggestions from code review Co-Authored-By: wjwwood <william+github@osrfoundation.org> * include what you use (cpplint) Signed-off-by: William Woodall <william@osrfoundation.org> * avoid redundant use of SignalHandler:: Signed-off-by: William Woodall <william@osrfoundation.org> * Update rclcpp/src/rclcpp/signal_handler.hpp Co-Authored-By: wjwwood <william+github@osrfoundation.org> * fix Windows build Signed-off-by: William Woodall <william@osrfoundation.org> * actually fix Windows Signed-off-by: William Woodall <william@osrfoundation.org>
This commit is contained in:
parent
c93beb5d16
commit
2e58dde5ef
4 changed files with 548 additions and 264 deletions
|
@ -65,6 +65,7 @@ set(${PROJECT_NAME}_SRCS
|
|||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/publisher.cpp
|
||||
src/rclcpp/service.cpp
|
||||
src/rclcpp/signal_handler.cpp
|
||||
src/rclcpp/subscription.cpp
|
||||
src/rclcpp/time.cpp
|
||||
src/rclcpp/time_source.cpp
|
||||
|
|
357
rclcpp/src/rclcpp/signal_handler.cpp
Normal file
357
rclcpp/src/rclcpp/signal_handler.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "./signal_handler.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// includes for semaphore notification code
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <dispatch/dispatch.h>
|
||||
#else // posix
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
using rclcpp::SignalHandler;
|
||||
|
||||
// initialize static storage in SignalHandler class
|
||||
SignalHandler::signal_handler_type SignalHandler::old_signal_handler_;
|
||||
std::atomic_bool SignalHandler::signal_received_ = ATOMIC_VAR_INIT(false);
|
||||
std::atomic_bool SignalHandler::wait_for_signal_is_setup_ = ATOMIC_VAR_INIT(false);
|
||||
#if defined(_WIN32)
|
||||
HANDLE SignalHandler::signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_t SignalHandler::signal_handler_sem_;
|
||||
#else // posix
|
||||
sem_t SignalHandler::signal_handler_sem_;
|
||||
#endif
|
||||
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
static SignalHandler signal_handler;
|
||||
return signal_handler;
|
||||
}
|
||||
|
||||
rclcpp::Logger &
|
||||
SignalHandler::get_logger()
|
||||
{
|
||||
static rclcpp::Logger logger = rclcpp::get_logger("rclcpp");
|
||||
return logger;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::install()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool already_installed = installed_.exchange(true);
|
||||
if (already_installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
setup_wait_for_signal();
|
||||
signal_received_.store(false);
|
||||
|
||||
SignalHandler::signal_handler_type signal_handler_argument;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
memset(&signal_handler_argument, 0, sizeof(signal_handler_argument));
|
||||
sigemptyset(&signal_handler_argument.sa_mask);
|
||||
signal_handler_argument.sa_sigaction = signal_handler;
|
||||
signal_handler_argument.sa_flags = SA_SIGINFO;
|
||||
#else
|
||||
signal_handler_argument = signal_handler;
|
||||
#endif
|
||||
|
||||
old_signal_handler_ = SignalHandler::set_signal_handler(SIGINT, signal_handler_argument);
|
||||
|
||||
signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
|
||||
} catch (...) {
|
||||
installed_.store(false);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "signal handler installed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::uninstall()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool installed = installed_.exchange(false);
|
||||
if (!installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
|
||||
// I think we need to assert that we're the current signal handler, and mitigate if not.
|
||||
set_signal_handler(SIGINT, old_signal_handler_);
|
||||
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
signal_handler_thread_.join();
|
||||
teardown_wait_for_signal();
|
||||
} catch (...) {
|
||||
installed_.exchange(true);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::is_installed()
|
||||
{
|
||||
return installed_.load();
|
||||
}
|
||||
|
||||
SignalHandler::~SignalHandler()
|
||||
{
|
||||
try {
|
||||
uninstall();
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught %s exception when uninstalling the sigint handler in rclcpp::~SignalHandler: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught unknown exception when uninstalling the sigint handler in rclcpp::~SignalHandler");
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
void
|
||||
__safe_strerror(int errnum, char * buffer, size_t buffer_length)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
strerror_s(buffer, buffer_length, errnum);
|
||||
#elif (defined(_GNU_SOURCE) && !defined(ANDROID))
|
||||
char * msg = strerror_r(errnum, buffer, buffer_length);
|
||||
if (msg != buffer) {
|
||||
strncpy(buffer, msg, buffer_length);
|
||||
buffer[buffer_length - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
int error_status = strerror_r(errnum, buffer, buffer_length);
|
||||
if (error_status != 0) {
|
||||
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errnum));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SignalHandler::signal_handler_type
|
||||
SignalHandler::set_signal_handler(
|
||||
int signal_value,
|
||||
const SignalHandler::signal_handler_type & signal_handler)
|
||||
{
|
||||
bool signal_handler_install_failed;
|
||||
SignalHandler::signal_handler_type old_signal_handler;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
|
||||
signal_handler_install_failed = (ret == -1);
|
||||
#else
|
||||
old_signal_handler = std::signal(signal_value, signal_handler);
|
||||
signal_handler_install_failed = (old_signal_handler == SIG_ERR);
|
||||
#endif
|
||||
if (signal_handler_install_failed) {
|
||||
char error_string[1024];
|
||||
__safe_strerror(errno, error_string, sizeof(error_string));
|
||||
auto msg =
|
||||
"Failed to set SIGINT signal handler (" + std::to_string(errno) + "): " + error_string;
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
return old_signal_handler;
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::signal_handler_common()
|
||||
{
|
||||
signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): SIGINT received, notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
}
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_.sa_flags & SA_SIGINFO) {
|
||||
if (old_signal_handler_.sa_sigaction != NULL) {
|
||||
old_signal_handler_.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
old_signal_handler_.sa_handler != NULL && // Is set
|
||||
old_signal_handler_.sa_handler != SIG_DFL && // Is not default
|
||||
old_signal_handler_.sa_handler != SIG_IGN) // Is not ignored
|
||||
{
|
||||
old_signal_handler_.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#else
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_) {
|
||||
old_signal_handler_(signal_value);
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): SIGINT received, shutting down");
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_sigint) {
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"deferred_signal_handler(): "
|
||||
"shutting down rclcpp::Context @ %p, because it had shutdown_on_sigint == true",
|
||||
context_ptr.get());
|
||||
context_ptr->shutdown("signal handler");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_installed()) {
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
|
||||
break;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): waiting for SIGINT or uninstall");
|
||||
wait_for_signal();
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): woken up due to SIGINT or uninstall");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::setup_wait_for_signal()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
signal_handler_sem_ = CreateSemaphore(
|
||||
NULL, // default security attributes
|
||||
0, // initial semaphore count
|
||||
1, // maximum semaphore count
|
||||
NULL); // unnamed semaphore
|
||||
if (NULL == signal_handler_sem_) {
|
||||
throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
signal_handler_sem_ = dispatch_semaphore_create(0);
|
||||
#else // posix
|
||||
if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
|
||||
throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
|
||||
}
|
||||
#endif
|
||||
wait_for_signal_is_setup_.store(true);
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::teardown_wait_for_signal() noexcept
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(signal_handler_sem_);
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_release(signal_handler_sem_);
|
||||
#else // posix
|
||||
if (-1 == sem_destroy(&signal_handler_sem_)) {
|
||||
RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::wait_for_signal()
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.load()) {
|
||||
RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
|
||||
switch (dw_wait_result) {
|
||||
case WAIT_ABANDONED:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
|
||||
GetLastError());
|
||||
break;
|
||||
case WAIT_OBJECT_0:
|
||||
// successful
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
|
||||
break;
|
||||
default:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
|
||||
GetLastError());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
|
||||
#else // posix
|
||||
int s;
|
||||
do {
|
||||
s = sem_wait(&signal_handler_sem_);
|
||||
} while (-1 == s && EINTR == errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::notify_signal_handler() noexcept
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.load()) {
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_signal(signal_handler_sem_);
|
||||
#else // posix
|
||||
if (-1 == sem_post(&signal_handler_sem_)) {
|
||||
RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
|
||||
}
|
||||
#endif
|
||||
}
|
189
rclcpp/src/rclcpp/signal_handler.hpp
Normal file
189
rclcpp/src/rclcpp/signal_handler.hpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Copyright 2018 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef RCLCPP__SIGNAL_HANDLER_HPP_
|
||||
#define RCLCPP__SIGNAL_HANDLER_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
// includes for semaphore notification code
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <dispatch/dispatch.h>
|
||||
#else // posix
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
// Determine if sigaction is available
|
||||
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||
#define RCLCPP_HAS_SIGACTION
|
||||
#endif
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Responsible for manaaging the SIGINT signal handling.
|
||||
/**
|
||||
* This class is responsible for:
|
||||
*
|
||||
* - installing the signal handler for SIGINT
|
||||
* - uninstalling the signal handler for SIGINT
|
||||
* - creating a thread to execute "on sigint" work outside of the signal handler
|
||||
* - safely notifying the dedicated signal handling thread when receiving SIGINT
|
||||
* - implementation of all of the signal handling work, like shutting down contexts
|
||||
*
|
||||
* \internal
|
||||
*/
|
||||
class SignalHandler final
|
||||
{
|
||||
public:
|
||||
/// Return the global singleton of this class.
|
||||
static
|
||||
SignalHandler &
|
||||
get_global_signal_handler();
|
||||
|
||||
/// Return a global singleton logger to avoid needing to create it everywhere.
|
||||
static
|
||||
rclcpp::Logger &
|
||||
get_logger();
|
||||
|
||||
/// Install the signal handler for SIGINT and start the dedicated signal handling thread.
|
||||
/**
|
||||
* Also stores the current signal handler to be called on SIGINT and to
|
||||
* restore when uninstalling this signal handler.
|
||||
*/
|
||||
bool
|
||||
install();
|
||||
|
||||
/// Uninstall the signal handler for SIGINT and join the dedicated singal handling thread.
|
||||
/**
|
||||
* Also restores the previous signal handler.
|
||||
*/
|
||||
bool
|
||||
uninstall();
|
||||
|
||||
/// Return true if installed, false otherwise.
|
||||
bool
|
||||
is_installed();
|
||||
|
||||
private:
|
||||
SignalHandler() = default;
|
||||
|
||||
~SignalHandler();
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
using signal_handler_type = struct sigaction;
|
||||
#else
|
||||
using signal_handler_type = void (*)(int);
|
||||
#endif
|
||||
// POSIX signal handler structure storage for the existing signal handler.
|
||||
static SignalHandler::signal_handler_type old_signal_handler_;
|
||||
|
||||
/// Set the signal handler function.
|
||||
static
|
||||
SignalHandler::signal_handler_type
|
||||
set_signal_handler(int signal_value, const SignalHandler::signal_handler_type & signal_handler);
|
||||
|
||||
/// Common signal handler code between sigaction and non-sigaction versions.
|
||||
static
|
||||
void
|
||||
signal_handler_common();
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
/// Signal handler function.
|
||||
static
|
||||
void
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context);
|
||||
#else
|
||||
/// Signal handler function.
|
||||
static
|
||||
void
|
||||
signal_handler(int signal_value);
|
||||
#endif
|
||||
|
||||
/// Target of the dedicated signal handling thread.
|
||||
void
|
||||
deferred_signal_handler();
|
||||
|
||||
/// Setup anything that is necessary for wait_for_signal() or notify_signal_handler().
|
||||
/**
|
||||
* This must be called before wait_for_signal() or notify_signal_handler().
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
setup_wait_for_signal();
|
||||
|
||||
/// Undo all setup done in setup_wait_for_signal().
|
||||
/**
|
||||
* Must not call wait_for_signal() or notify_signal_handler() after calling this.
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
teardown_wait_for_signal() noexcept;
|
||||
|
||||
/// Wait for a notification from notify_signal_handler() in a signal safe way.
|
||||
/**
|
||||
* This static method may throw if posting the semaphore fails.
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
wait_for_signal();
|
||||
|
||||
/// Notify blocking wait_for_signal() calls in a signal safe way.
|
||||
/**
|
||||
* This is used to notify the deferred_signal_handler() thread to start work
|
||||
* from the signal handler.
|
||||
*
|
||||
* This is thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
notify_signal_handler() noexcept;
|
||||
|
||||
// Whether or not a signal has been received.
|
||||
static std::atomic_bool signal_received_;
|
||||
// A thread to which singal handling tasks are deferred.
|
||||
std::thread signal_handler_thread_;
|
||||
|
||||
// A mutex used to synchronize the install() and uninstall() methods.
|
||||
std::mutex install_mutex_;
|
||||
// Whether or not the signal handler has been installed.
|
||||
std::atomic_bool installed_{false};
|
||||
|
||||
// Whether or not the semaphore for wait_for_signal is setup.
|
||||
static std::atomic_bool wait_for_signal_is_setup_;
|
||||
// Storage for the wait_for_signal semaphore.
|
||||
#if defined(_WIN32)
|
||||
static HANDLE signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
static dispatch_semaphore_t signal_handler_sem_;
|
||||
#else // posix
|
||||
static sem_t signal_handler_sem_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SIGNAL_HANDLER_HPP_
|
|
@ -14,279 +14,16 @@
|
|||
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "./signal_handler.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
// Determine if sigaction is available
|
||||
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||
#define HAS_SIGACTION
|
||||
#endif
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class SignalHandler final
|
||||
{
|
||||
public:
|
||||
static
|
||||
SignalHandler &
|
||||
get_global_signal_handler()
|
||||
{
|
||||
static SignalHandler singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
bool
|
||||
install()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool already_installed = installed_.exchange(true);
|
||||
if (already_installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
signal_received.exchange(false);
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = signal_handler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
old_action = set_sigaction(SIGINT, action);
|
||||
#else
|
||||
old_signal_handler = set_signal_handler(SIGINT, signal_handler);
|
||||
#endif
|
||||
signal_handler_thread_ =
|
||||
std::thread(&SignalHandler::deferred_signal_handler, this);
|
||||
} catch (...) {
|
||||
installed_.exchange(false);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler installed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
uninstall()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool installed = installed_.exchange(false);
|
||||
if (!installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
#ifdef HAS_SIGACTION
|
||||
set_sigaction(SIGINT, old_action);
|
||||
#else
|
||||
set_signal_handler(SIGINT, old_signal_handler);
|
||||
#endif
|
||||
events_condition_variable.notify_one();
|
||||
signal_handler_thread_.join();
|
||||
} catch (...) {
|
||||
installed_.exchange(true);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "signal handler uninstalled");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_installed()
|
||||
{
|
||||
return installed_.load();
|
||||
}
|
||||
|
||||
private:
|
||||
SignalHandler() = default;
|
||||
|
||||
~SignalHandler()
|
||||
{
|
||||
try {
|
||||
uninstall();
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught %s exception when uninstalling the sigint handler in rclcpp::~SignalHandler: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught unknown exception when uninstalling the sigint handler in rclcpp::~SignalHandler");
|
||||
}
|
||||
}
|
||||
|
||||
// POSIX signal handler structure.
|
||||
#ifdef HAS_SIGACTION
|
||||
static struct sigaction old_action;
|
||||
#else
|
||||
typedef void (* signal_handler_t)(int);
|
||||
static signal_handler_t old_signal_handler;
|
||||
#endif
|
||||
|
||||
static
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction
|
||||
set_sigaction(int signal_value, const struct sigaction & action)
|
||||
#else
|
||||
signal_handler_t
|
||||
set_signal_handler(int signal_value, signal_handler_t signal_handler)
|
||||
#endif
|
||||
{
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction old_action;
|
||||
ssize_t ret = sigaction(signal_value, &action, &old_action);
|
||||
if (ret == -1)
|
||||
#else
|
||||
signal_handler_t old_signal_handler = std::signal(signal_value, signal_handler);
|
||||
// NOLINTNEXTLINE(readability/braces)
|
||||
if (old_signal_handler == SIG_ERR)
|
||||
#endif
|
||||
{
|
||||
const size_t error_length = 1024;
|
||||
// NOLINTNEXTLINE(runtime/arrays)
|
||||
char error_string[error_length];
|
||||
#ifndef _WIN32
|
||||
#if (defined(_GNU_SOURCE) && !defined(ANDROID))
|
||||
char * msg = strerror_r(errno, error_string, error_length);
|
||||
if (msg != error_string) {
|
||||
strncpy(error_string, msg, error_length);
|
||||
msg[error_length - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
int error_status = strerror_r(errno, error_string, error_length);
|
||||
if (error_status != 0) {
|
||||
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errno));
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
strerror_s(error_string, error_length, errno);
|
||||
#endif
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
|
||||
error_string);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
return old_action;
|
||||
#else
|
||||
return old_signal_handler;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
#ifdef HAS_SIGACTION
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
#else
|
||||
signal_handler(int signal_value)
|
||||
#endif
|
||||
{
|
||||
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
if (old_action.sa_flags & SA_SIGINFO) {
|
||||
if (old_action.sa_sigaction != NULL) {
|
||||
old_action.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
old_action.sa_handler != NULL && // Is set
|
||||
old_action.sa_handler != SIG_DFL && // Is not default
|
||||
old_action.sa_handler != SIG_IGN) // Is not ignored
|
||||
{
|
||||
old_action.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (old_signal_handler) {
|
||||
old_signal_handler(signal_value);
|
||||
}
|
||||
#endif
|
||||
signal_received.exchange(true);
|
||||
events_condition_variable.notify_one();
|
||||
}
|
||||
|
||||
void
|
||||
deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received.exchange(false)) {
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_sigint) {
|
||||
context_ptr->shutdown("signal handler");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_installed()) {
|
||||
break;
|
||||
}
|
||||
std::unique_lock<std::mutex> events_lock(events_mutex);
|
||||
events_condition_variable.wait(events_lock);
|
||||
}
|
||||
}
|
||||
|
||||
// A mutex to lock event signaling.
|
||||
static std::mutex events_mutex;
|
||||
// A cond var to wait on for event signaling.
|
||||
static std::condition_variable events_condition_variable;
|
||||
// Whether a signal has been received or not.
|
||||
static std::atomic<bool> signal_received;
|
||||
// A thread to defer signal handling tasks to.
|
||||
std::thread signal_handler_thread_;
|
||||
|
||||
// A mutex to synchronize the install() and uninstall() methods.
|
||||
std::mutex install_mutex_;
|
||||
// Whether this handler has been installed or not.
|
||||
std::atomic<bool> installed_{false};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
// Declare static class variables for SignalHandler
|
||||
std::mutex rclcpp::SignalHandler::events_mutex;
|
||||
std::condition_variable rclcpp::SignalHandler::events_condition_variable;
|
||||
std::atomic<bool> rclcpp::SignalHandler::signal_received;
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction rclcpp::SignalHandler::old_action;
|
||||
#else
|
||||
typedef void (* signal_handler_t)(int);
|
||||
signal_handler_t rclcpp::SignalHandler::old_signal_handler = 0;
|
||||
#endif
|
||||
|
||||
/// Mutex to protect g_sigint_guard_cond_handles
|
||||
static std::mutex g_sigint_guard_cond_handles_mutex;
|
||||
/// Guard conditions for interrupting the rmw implementation when the global interrupt signal fired.
|
||||
static std::map<rcl_wait_set_t *, rcl_guard_condition_t> g_sigint_guard_cond_handles;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
static std::condition_variable g_interrupt_condition_variable;
|
||||
/// Mutex for protecting the global condition variable.
|
||||
static std::mutex g_interrupt_mutex;
|
||||
|
||||
void
|
||||
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue