ROS 2 rclcpp + rcl - humble  humble
ROS 2 C++ Client Library with ROS Client Library
signal_handler.cpp
1 // Copyright 2018 Open Source Robotics Foundation, Inc.
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 
15 #include "signal_handler.hpp"
16 
17 #include <atomic>
18 #include <csignal>
19 #include <exception>
20 #include <mutex>
21 #include <string>
22 #include <thread>
23 
24 // includes for semaphore notification code
25 #if defined(_WIN32)
26 #include <windows.h>
27 #elif defined(__APPLE__)
28 #include <dispatch/dispatch.h>
29 #else // posix
30 #include <semaphore.h>
31 #endif
32 
33 #include "rclcpp/logging.hpp"
34 #include "rclcpp/utilities.hpp"
35 #include "rcutils/strerror.h"
36 #include "rmw/impl/cpp/demangle.hpp"
37 
40 
41 SignalHandler::signal_handler_type
42 SignalHandler::set_signal_handler(
43  int signal_value,
44  const SignalHandler::signal_handler_type & signal_handler)
45 {
46  bool signal_handler_install_failed;
47  SignalHandler::signal_handler_type old_signal_handler;
48 #if defined(RCLCPP_HAS_SIGACTION)
49  ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
50  signal_handler_install_failed = (ret == -1);
51 #else
52  old_signal_handler = std::signal(signal_value, signal_handler);
53  signal_handler_install_failed = (old_signal_handler == SIG_ERR);
54 #endif
55  if (signal_handler_install_failed) {
56  char error_string[1024];
57  rcutils_strerror(error_string, sizeof(error_string));
58  auto msg =
59  "Failed to set signal handler (" + std::to_string(errno) + "): " + error_string;
60  throw std::runtime_error(msg);
61  }
62  return old_signal_handler;
63 }
64 
65 // Unfortunately macros (or duplicated code) are needed here,
66 // as the signal handler must be a function pointer.
67 #if defined(RCLCPP_HAS_SIGACTION)
68 void
69 SignalHandler::signal_handler(
70  int signum, siginfo_t * siginfo, void * context)
71 {
72  auto & instance = SignalHandler::get_global_signal_handler();
73 
74  auto old_signal_handler = instance.get_old_signal_handler(signum);
75  if (old_signal_handler.sa_flags & SA_SIGINFO) {
76  if (old_signal_handler.sa_sigaction != NULL) {
77  old_signal_handler.sa_sigaction(signum, siginfo, context);
78  }
79  } else {
80  if (
81  old_signal_handler.sa_handler != NULL && /* Is set */
82  old_signal_handler.sa_handler != SIG_DFL && /* Is not default*/
83  old_signal_handler.sa_handler != SIG_IGN) /* Is not ignored */
84  {
85  old_signal_handler.sa_handler(signum);
86  }
87  }
88  instance.signal_handler_common();
89 }
90 #else
91 void
92 SignalHandler::signal_handler(int signum)
93 {
94  auto & instance = SignalHandler::get_global_signal_handler();
95  auto old_signal_handler = instance.get_old_signal_handler(signum);
96  if (
97  SIG_ERR != old_signal_handler && SIG_IGN != old_signal_handler &&
98  SIG_DFL != old_signal_handler)
99  {
100  old_signal_handler(signum);
101  }
102  instance.signal_handler_common();
103 }
104 #endif
105 
108 {
110 }
111 
114 {
115  static SignalHandler & signal_handler = *new SignalHandler();
116  return signal_handler;
117 }
118 
119 bool
121 {
122  std::lock_guard<std::mutex> lock(install_mutex_);
123  bool already_installed = installed_.exchange(true);
124  if (already_installed) {
125  return false;
126  }
127  if (signal_handler_options == SignalHandlerOptions::None) {
128  return true;
129  }
130  signal_handlers_options_ = signal_handler_options;
131  try {
132  setup_wait_for_signal();
133  signal_received_.store(false);
134 
135  SignalHandler::signal_handler_type handler_argument;
136 #if defined(RCLCPP_HAS_SIGACTION)
137  memset(&handler_argument, 0, sizeof(handler_argument));
138  sigemptyset(&handler_argument.sa_mask);
139  handler_argument.sa_sigaction = &this->signal_handler;
140  handler_argument.sa_flags = SA_SIGINFO;
141 #else
142  handler_argument = &this->signal_handler;
143 #endif
144  if (
145  signal_handler_options == SignalHandlerOptions::SigInt ||
146  signal_handler_options == SignalHandlerOptions::All)
147  {
148  old_sigint_handler_ = set_signal_handler(SIGINT, handler_argument);
149  }
150 
151  if (
152  signal_handler_options == SignalHandlerOptions::SigTerm ||
153  signal_handler_options == SignalHandlerOptions::All)
154  {
155  old_sigterm_handler_ = set_signal_handler(SIGTERM, handler_argument);
156  }
157 
158  signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
159  } catch (...) {
160  installed_.store(false);
161  throw;
162  }
163  RCLCPP_DEBUG(get_logger(), "signal handler installed");
164  return true;
165 }
166 
167 bool
169 {
170  std::lock_guard<std::mutex> lock(install_mutex_);
171  bool installed = installed_.exchange(false);
172  if (!installed) {
173  return false;
174  }
175  try {
176  // TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
177  // I think we need to assert that we're the current signal handler, and mitigate if not.
178  if (
179  SignalHandlerOptions::SigInt == signal_handlers_options_ ||
180  SignalHandlerOptions::All == signal_handlers_options_)
181  {
182  set_signal_handler(SIGINT, old_sigint_handler_);
183  }
184  if (
185  SignalHandlerOptions::SigTerm == signal_handlers_options_ ||
186  SignalHandlerOptions::All == signal_handlers_options_)
187  {
188  set_signal_handler(SIGTERM, old_sigterm_handler_);
189  }
190  signal_handlers_options_ = SignalHandlerOptions::None;
191  RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
192  notify_signal_handler();
193  if (signal_handler_thread_.joinable()) {
194  signal_handler_thread_.join();
195  }
196  teardown_wait_for_signal();
197  } catch (...) {
198  installed_.exchange(true);
199  throw;
200  }
201  RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
202  return true;
203 }
204 
205 bool
207 {
208  return installed_.load();
209 }
210 
211 SignalHandler::~SignalHandler()
212 {
213  try {
214  uninstall();
215  } catch (const std::exception & exc) {
216  RCLCPP_ERROR(
217  get_logger(),
218  "caught %s exception when uninstalling signal handlers in rclcpp::~SignalHandler: %s",
219  rmw::impl::cpp::demangle(exc).c_str(), exc.what());
220  } catch (...) {
221  RCLCPP_ERROR(
222  get_logger(),
223  "caught unknown exception when uninstalling signal handlers in rclcpp::~SignalHandler");
224  }
225 }
226 
227 SignalHandler::signal_handler_type
228 SignalHandler::get_old_signal_handler(int signum)
229 {
230  if (SIGINT == signum) {
231  return old_sigint_handler_;
232  } else if (SIGTERM == signum) {
233  return old_sigterm_handler_;
234  }
235 #if defined(RCLCPP_HAS_SIGACTION)
236  SignalHandler::signal_handler_type ret;
237  memset(&ret, 0, sizeof(ret));
238  sigemptyset(&ret.sa_mask);
239  ret.sa_handler = SIG_DFL;
240  return ret;
241 #else
242  return SIG_DFL;
243 #endif
244 }
245 
246 void
247 SignalHandler::signal_handler_common()
248 {
249  auto & instance = SignalHandler::get_global_signal_handler();
250  instance.signal_received_.store(true);
251  instance.notify_signal_handler();
252 }
253 
254 void
255 SignalHandler::deferred_signal_handler()
256 {
257  while (true) {
258  if (signal_received_.exchange(false)) {
259  RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGINT/SIGTERM)");
260  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
261  for (auto context_ptr : rclcpp::get_contexts()) {
262  if (context_ptr->get_init_options().shutdown_on_signal) {
263  RCLCPP_DEBUG(
264  get_logger(),
265  "deferred_signal_handler(): "
266  "shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
267  static_cast<void *>(context_ptr.get()));
268  try {
269  context_ptr->shutdown("signal handler");
270  } catch (const std::exception & exc) {
271  // an uncaught exception on this thread would call std::terminate(),
272  // taking down the whole process, so log the failure instead
273  RCLCPP_ERROR(
274  get_logger(),
275  "deferred_signal_handler(): failed to shutdown rclcpp::Context @ %p: %s",
276  static_cast<void *>(context_ptr.get()), exc.what());
277  } catch (...) {
278  RCLCPP_ERROR(
279  get_logger(),
280  "deferred_signal_handler(): failed to shutdown rclcpp::Context @ %p",
281  static_cast<void *>(context_ptr.get()));
282  }
283  }
284  }
285  }
286  if (!is_installed()) {
287  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
288  break;
289  }
290  RCLCPP_DEBUG(
291  get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
292  wait_for_signal();
293  RCLCPP_DEBUG(
294  get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
295  }
296 }
297 
298 void
299 SignalHandler::setup_wait_for_signal()
300 {
301 #if defined(_WIN32)
302  signal_handler_sem_ = CreateSemaphore(
303  NULL, // default security attributes
304  0, // initial semaphore count
305  1, // maximum semaphore count
306  NULL); // unnamed semaphore
307  if (NULL == signal_handler_sem_) {
308  throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
309  }
310 #elif defined(__APPLE__)
311  signal_handler_sem_ = dispatch_semaphore_create(0);
312 #else // posix
313  if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
314  throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
315  }
316 #endif
317  wait_for_signal_is_setup_.store(true);
318 }
319 
320 void
321 SignalHandler::teardown_wait_for_signal() noexcept
322 {
323  if (!wait_for_signal_is_setup_.exchange(false)) {
324  return;
325  }
326 #if defined(_WIN32)
327  CloseHandle(signal_handler_sem_);
328 #elif defined(__APPLE__)
329  dispatch_release(signal_handler_sem_);
330 #else // posix
331  if (-1 == sem_destroy(&signal_handler_sem_)) {
332  RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
333  }
334 #endif
335 }
336 
337 void
338 SignalHandler::wait_for_signal()
339 {
340  if (!wait_for_signal_is_setup_.load()) {
341  RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
342  return;
343  }
344 #if defined(_WIN32)
345  DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
346  switch (dw_wait_result) {
347  case WAIT_ABANDONED:
348  RCLCPP_ERROR(
349  get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
350  GetLastError());
351  break;
352  case WAIT_OBJECT_0:
353  // successful
354  break;
355  case WAIT_TIMEOUT:
356  RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
357  break;
358  case WAIT_FAILED:
359  RCLCPP_ERROR(
360  get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
361  break;
362  default:
363  RCLCPP_ERROR(
364  get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
365  GetLastError());
366  }
367 #elif defined(__APPLE__)
368  dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
369 #else // posix
370  int s;
371  do {
372  s = sem_wait(&signal_handler_sem_);
373  } while (-1 == s && EINTR == errno);
374 #endif
375 }
376 
377 void
378 SignalHandler::notify_signal_handler() noexcept
379 {
380  if (!wait_for_signal_is_setup_.load()) {
381  return;
382  }
383 #if defined(_WIN32)
384  if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
385  RCLCPP_ERROR(
386  get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
387  }
388 #elif defined(__APPLE__)
389  dispatch_semaphore_signal(signal_handler_sem_);
390 #else // posix
391  if (-1 == sem_post(&signal_handler_sem_)) {
392  RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
393  }
394 #endif
395 }
396 
399 {
400  return signal_handlers_options_;
401 }
Responsible for managing the SIGINT/SIGTERM signal handling.
static rclcpp::Logger & get_logger()
Return a global singleton logger to avoid needing to create it everywhere.
bool is_installed()
Return true if installed, false otherwise.
static SignalHandler & get_global_signal_handler()
Return the global singleton of this class.
bool install(SignalHandlerOptions signal_handler_options=SignalHandlerOptions::All)
Install the signal handler for SIGINT/SIGTERM and start the dedicated signal handling thread.
rclcpp::SignalHandlerOptions get_current_signal_handler_options()
Get the current signal handler options.
SignalHandlerOptions
Option to indicate which signal handlers rclcpp should install.
Definition: utilities.hpp:48
@ SigTerm
Install only a sigterm handler.
@ None
Do not install any signal handler.
@ All
Install both sigint and sigterm, this is the default behavior.
@ SigInt
Install only a sigint handler.
RCLCPP_PUBLIC std::vector< Context::SharedPtr > get_contexts()
Return a copy of the list of context shared pointers.
Definition: context.cpp:547