Skip to content

Commit edee4fc

Browse files
Janosch MachowinskiJanosch Machowinski
authored andcommitted
refactor: Use std::conditional_variable in signal handler code
Code cleanup to use std::conditional_variable in the signal handler code instead of a custom home brew version. Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
1 parent 41c7f68 commit edee4fc

File tree

2 files changed

+115
-199
lines changed

2 files changed

+115
-199
lines changed

rclcpp/src/rclcpp/signal_handler.cpp

Lines changed: 92 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,12 @@
1616

1717
#include <atomic>
1818
#include <csignal>
19+
#include <future>
20+
#include <optional>
1921
#include <mutex>
2022
#include <string>
2123
#include <thread>
2224

23-
// includes for semaphore notification code
24-
#if defined(_WIN32)
25-
#include <windows.h>
26-
#elif defined(__APPLE__)
27-
#include <dispatch/dispatch.h>
28-
#else // posix
29-
#include <semaphore.h>
30-
#endif
31-
3225
#include "rclcpp/logging.hpp"
3326
#include "rclcpp/utilities.hpp"
3427
#include "rcutils/strerror.h"
@@ -102,6 +95,21 @@ SignalHandler::signal_handler(int signum)
10295
}
10396
#endif
10497

98+
void rclcpp::SignalHandler::signal_handler_common(int signum) noexcept
99+
{
100+
switch(signum) {
101+
case SIGTERM:
102+
notify_deferred_handler(Input::SigTerm);
103+
break;
104+
case SIGINT:
105+
notify_deferred_handler(Input::SigInt);
106+
break;
107+
default:
108+
break;
109+
}
110+
}
111+
112+
105113
rclcpp::Logger &
106114
SignalHandler::get_logger()
107115
{
@@ -119,18 +127,20 @@ bool
119127
SignalHandler::install(SignalHandlerOptions signal_handler_options)
120128
{
121129
std::lock_guard<std::mutex> lock(install_mutex_);
122-
bool already_installed = installed_.exchange(true);
123-
if (already_installed) {
130+
if (installed_) {
124131
return false;
125132
}
126133
if (signal_handler_options == SignalHandlerOptions::None) {
127134
return true;
128135
}
136+
137+
// Reset state in case someone uninstalls and reinstall handlers
138+
got_sig_int = false;
139+
got_sig_term = false;
140+
terminate_handler_ = false;
141+
129142
signal_handlers_options_ = signal_handler_options;
130143
try {
131-
setup_wait_for_signal();
132-
signal_received_.store(false);
133-
134144
SignalHandler::signal_handler_type handler_argument;
135145
#if defined(RCLCPP_HAS_SIGACTION)
136146
memset(&handler_argument, 0, sizeof(handler_argument));
@@ -156,9 +166,10 @@ SignalHandler::install(SignalHandlerOptions signal_handler_options)
156166

157167
signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
158168
} catch (...) {
159-
installed_.store(false);
160169
throw;
161170
}
171+
installed_ = true;
172+
162173
RCLCPP_DEBUG(get_logger(), "signal handler installed");
163174
return true;
164175
}
@@ -167,11 +178,18 @@ bool
167178
SignalHandler::uninstall()
168179
{
169180
std::lock_guard<std::mutex> lock(install_mutex_);
170-
bool installed = installed_.exchange(false);
171-
if (!installed) {
181+
if (!installed_) {
172182
return false;
173183
}
184+
185+
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): shutting down deferred signal handler");
186+
notify_deferred_handler(Input::TerminateHandler);
187+
if (signal_handler_thread_.joinable()) {
188+
signal_handler_thread_.join();
189+
}
190+
174191
try {
192+
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): restoring signal handlers");
175193
// TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
176194
// I think we need to assert that we're the current signal handler, and mitigate if not.
177195
if (
@@ -187,24 +205,19 @@ SignalHandler::uninstall()
187205
set_signal_handler(SIGTERM, old_sigterm_handler_);
188206
}
189207
signal_handlers_options_ = SignalHandlerOptions::None;
190-
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
191-
notify_signal_handler();
192-
if (signal_handler_thread_.joinable()) {
193-
signal_handler_thread_.join();
194-
}
195-
teardown_wait_for_signal();
196208
} catch (...) {
197-
installed_.exchange(true);
198209
throw;
199210
}
211+
installed_ = false;
200212
RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
201213
return true;
202214
}
203215

204216
bool
205217
SignalHandler::is_installed()
206218
{
207-
return installed_.load();
219+
std::lock_guard<std::mutex> lock(install_mutex_);
220+
return installed_;
208221
}
209222

210223
SignalHandler::~SignalHandler()
@@ -242,143 +255,79 @@ SignalHandler::get_old_signal_handler(int signum)
242255
#endif
243256
}
244257

245-
void
246-
SignalHandler::signal_handler_common(int signum)
247-
{
248-
auto & instance = SignalHandler::get_global_signal_handler();
249-
instance.signal_number_.store(signum);
250-
instance.signal_received_.store(true);
251-
instance.notify_signal_handler();
252-
}
253-
254258
void
255259
SignalHandler::deferred_signal_handler()
256260
{
257-
while (true) {
258-
if (signal_received_.exchange(false)) {
259-
int signum = signal_number_.load();
260-
RCLCPP_INFO(get_logger(), "signal_handler(signum=%d)", signum);
261-
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
262-
for (auto context_ptr : rclcpp::get_contexts()) {
263-
if (context_ptr->get_init_options().shutdown_on_signal) {
264-
RCLCPP_DEBUG(
261+
bool running = true;
262+
while (running) {
263+
std::optional<Input> next;
264+
265+
RCLCPP_DEBUG(
266+
get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
267+
{
268+
std::unique_lock l(signal_mutex_);
269+
signal_conditional_.wait(l, [this] () {
270+
return terminate_handler_ || got_sig_int || got_sig_term;
271+
});
272+
273+
if(terminate_handler_.exchange(false)) {
274+
next = Input::TerminateHandler;
275+
}
276+
if(got_sig_int.exchange(false)) {
277+
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGINT)");
278+
next = Input::SigInt;
279+
}
280+
if(got_sig_term.exchange(false)) {
281+
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGTERM)");
282+
next = Input::SigTerm;
283+
}
284+
}
285+
RCLCPP_DEBUG(
286+
get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
287+
288+
// Note 'next' must always be valid at this point, if not
289+
// a throw is the expected behaviour
290+
switch(next.value()) {
291+
case Input::SigInt:
292+
[[fallthrough]];
293+
case Input::SigTerm:
294+
{
295+
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
296+
for (auto context_ptr : rclcpp::get_contexts()) {
297+
if (context_ptr->get_init_options().shutdown_on_signal) {
298+
RCLCPP_DEBUG(
265299
get_logger(),
266300
"deferred_signal_handler(): "
267301
"shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
268302
static_cast<void *>(context_ptr.get()));
269-
context_ptr->shutdown("signal handler");
303+
context_ptr->shutdown("signal handler");
304+
}
305+
}
306+
break;
270307
}
271-
}
272-
}
273-
if (!is_installed()) {
274-
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
275-
break;
308+
case Input::TerminateHandler:
309+
running = false;
310+
break;
276311
}
277-
RCLCPP_DEBUG(
278-
get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
279-
wait_for_signal();
280-
RCLCPP_DEBUG(
281-
get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
282312
}
283313
}
284314

285315
void
286-
SignalHandler::setup_wait_for_signal()
316+
SignalHandler::notify_deferred_handler(Input input) noexcept
287317
{
288-
#if defined(_WIN32)
289-
signal_handler_sem_ = CreateSemaphore(
290-
NULL, // default security attributes
291-
0, // initial semaphore count
292-
1, // maximum semaphore count
293-
NULL); // unnamed semaphore
294-
if (NULL == signal_handler_sem_) {
295-
throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
296-
}
297-
#elif defined(__APPLE__)
298-
signal_handler_sem_ = dispatch_semaphore_create(0);
299-
#else // posix
300-
if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
301-
throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
302-
}
303-
#endif
304-
wait_for_signal_is_setup_.store(true);
305-
}
306-
307-
void
308-
SignalHandler::teardown_wait_for_signal() noexcept
309-
{
310-
if (!wait_for_signal_is_setup_.exchange(false)) {
311-
return;
312-
}
313-
#if defined(_WIN32)
314-
CloseHandle(signal_handler_sem_);
315-
#elif defined(__APPLE__)
316-
dispatch_release(signal_handler_sem_);
317-
#else // posix
318-
if (-1 == sem_destroy(&signal_handler_sem_)) {
319-
RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
320-
}
321-
#endif
322-
}
323-
324-
void
325-
SignalHandler::wait_for_signal()
326-
{
327-
if (!wait_for_signal_is_setup_.load()) {
328-
RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
329-
return;
330-
}
331-
#if defined(_WIN32)
332-
DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
333-
switch (dw_wait_result) {
334-
case WAIT_ABANDONED:
335-
RCLCPP_ERROR(
336-
get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
337-
GetLastError());
338-
break;
339-
case WAIT_OBJECT_0:
340-
// successful
318+
switch(input) {
319+
case Input::SigInt:
320+
got_sig_int.exchange(true);
341321
break;
342-
case WAIT_TIMEOUT:
343-
RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
322+
case Input::SigTerm:
323+
got_sig_term.exchange(true);
344324
break;
345-
case WAIT_FAILED:
346-
RCLCPP_ERROR(
347-
get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
325+
case Input::TerminateHandler:
326+
terminate_handler_.exchange(true);
348327
break;
349-
default:
350-
RCLCPP_ERROR(
351-
get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
352-
GetLastError());
353328
}
354-
#elif defined(__APPLE__)
355-
dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
356-
#else // posix
357-
int s;
358-
do {
359-
s = sem_wait(&signal_handler_sem_);
360-
} while (-1 == s && EINTR == errno);
361-
#endif
362-
}
363329

364-
void
365-
SignalHandler::notify_signal_handler() noexcept
366-
{
367-
if (!wait_for_signal_is_setup_.load()) {
368-
return;
369-
}
370-
#if defined(_WIN32)
371-
if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
372-
RCLCPP_ERROR(
373-
get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
374-
}
375-
#elif defined(__APPLE__)
376-
dispatch_semaphore_signal(signal_handler_sem_);
377-
#else // posix
378-
if (-1 == sem_post(&signal_handler_sem_)) {
379-
RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
380-
}
381-
#endif
330+
signal_conditional_.notify_one();
382331
}
383332

384333
rclcpp::SignalHandlerOptions

0 commit comments

Comments
 (0)