ROS 2 rclcpp + rcl - lyrical  lyrical
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(signum);
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(signum);
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(int signum)
248 {
249  auto & instance = SignalHandler::get_global_signal_handler();
250  instance.signal_number_.store(signum);
251  instance.signal_received_.store(true);
252  instance.notify_signal_handler();
253 }
254 
255 void
256 SignalHandler::deferred_signal_handler()
257 {
258  while (true) {
259  if (signal_received_.exchange(false)) {
260  int signum = signal_number_.load();
261  RCLCPP_INFO(get_logger(), "signal_handler(signum=%d)", signum);
262  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
263  for (const auto & context_ptr : rclcpp::get_contexts()) {
264  if (context_ptr->get_init_options().shutdown_on_signal) {
265  RCLCPP_DEBUG(
266  get_logger(),
267  "deferred_signal_handler(): "
268  "shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
269  static_cast<void *>(context_ptr.get()));
270  try {
271  context_ptr->shutdown("signal handler");
272  } catch (const std::exception & exc) {
273  // an uncaught exception on this thread would call std::terminate(),
274  // taking down the whole process, so log the failure instead
275  RCLCPP_ERROR(
276  get_logger(),
277  "deferred_signal_handler(): failed to shutdown rclcpp::Context @ %p: %s",
278  static_cast<void *>(context_ptr.get()), exc.what());
279  } catch (...) {
280  RCLCPP_ERROR(
281  get_logger(),
282  "deferred_signal_handler(): failed to shutdown rclcpp::Context @ %p",
283  static_cast<void *>(context_ptr.get()));
284  }
285  }
286  }
287  }
288  if (!is_installed()) {
289  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
290  break;
291  }
292  RCLCPP_DEBUG(
293  get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
294  wait_for_signal();
295  RCLCPP_DEBUG(
296  get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
297  }
298 }
299 
300 void
301 SignalHandler::setup_wait_for_signal()
302 {
303 #if defined(_WIN32)
304  signal_handler_sem_ = CreateSemaphore(
305  NULL, // default security attributes
306  0, // initial semaphore count
307  1, // maximum semaphore count
308  NULL); // unnamed semaphore
309  if (NULL == signal_handler_sem_) {
310  throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
311  }
312 #elif defined(__APPLE__)
313  signal_handler_sem_ = dispatch_semaphore_create(0);
314 #else // posix
315  if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
316  throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
317  }
318 #endif
319  wait_for_signal_is_setup_.store(true);
320 }
321 
322 void
323 SignalHandler::teardown_wait_for_signal() noexcept
324 {
325  if (!wait_for_signal_is_setup_.exchange(false)) {
326  return;
327  }
328 #if defined(_WIN32)
329  CloseHandle(signal_handler_sem_);
330 #elif defined(__APPLE__)
331  dispatch_release(signal_handler_sem_);
332 #else // posix
333  if (-1 == sem_destroy(&signal_handler_sem_)) {
334  RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
335  }
336 #endif
337 }
338 
339 void
340 SignalHandler::wait_for_signal()
341 {
342  if (!wait_for_signal_is_setup_.load()) {
343  RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
344  return;
345  }
346 #if defined(_WIN32)
347  DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
348  switch (dw_wait_result) {
349  case WAIT_ABANDONED:
350  RCLCPP_ERROR(
351  get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
352  GetLastError());
353  break;
354  case WAIT_OBJECT_0:
355  // successful
356  break;
357  case WAIT_TIMEOUT:
358  RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
359  break;
360  case WAIT_FAILED:
361  RCLCPP_ERROR(
362  get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
363  break;
364  default:
365  RCLCPP_ERROR(
366  get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
367  GetLastError());
368  }
369 #elif defined(__APPLE__)
370  dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
371 #else // posix
372  int s;
373  do {
374  s = sem_wait(&signal_handler_sem_);
375  } while (-1 == s && EINTR == errno);
376 #endif
377 }
378 
379 void
380 SignalHandler::notify_signal_handler() noexcept
381 {
382  if (!wait_for_signal_is_setup_.load()) {
383  return;
384  }
385 #if defined(_WIN32)
386  if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
387  RCLCPP_ERROR(
388  get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
389  }
390 #elif defined(__APPLE__)
391  dispatch_semaphore_signal(signal_handler_sem_);
392 #else // posix
393  if (-1 == sem_post(&signal_handler_sem_)) {
394  RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
395  }
396 #endif
397 }
398 
401 {
402  return signal_handlers_options_;
403 }
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:49
@ 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:585