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