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/parameter_service.cpp
|
||||||
src/rclcpp/publisher.cpp
|
src/rclcpp/publisher.cpp
|
||||||
src/rclcpp/service.cpp
|
src/rclcpp/service.cpp
|
||||||
|
src/rclcpp/signal_handler.cpp
|
||||||
src/rclcpp/subscription.cpp
|
src/rclcpp/subscription.cpp
|
||||||
src/rclcpp/time.cpp
|
src/rclcpp/time.cpp
|
||||||
src/rclcpp/time_source.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 "rclcpp/utilities.hpp"
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <csignal>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "./signal_handler.hpp"
|
||||||
#include "rclcpp/contexts/default_context.hpp"
|
#include "rclcpp/contexts/default_context.hpp"
|
||||||
#include "rclcpp/logging.hpp"
|
|
||||||
#include "rclcpp/exceptions.hpp"
|
#include "rclcpp/exceptions.hpp"
|
||||||
#include "rclcpp/scope_exit.hpp"
|
|
||||||
|
|
||||||
#include "rcl/error_handling.h"
|
#include "rcl/error_handling.h"
|
||||||
#include "rcl/rcl.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
|
void
|
||||||
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue