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 <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(signum);
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(signum);
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(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 
254 void
255 SignalHandler::deferred_signal_handler()
256 {
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 (const auto & context_ptr : rclcpp::get_contexts()) {
263  if (context_ptr->get_init_options().shutdown_on_signal) {
264  RCLCPP_DEBUG(
265  get_logger(),
266  "deferred_signal_handler(): "
267  "shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
268  static_cast<void *>(context_ptr.get()));
269  context_ptr->shutdown("signal handler");
270  }
271  }
272  }
273  if (!is_installed()) {
274  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
275  break;
276  }
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");
282  }
283 }
284 
285 void
286 SignalHandler::setup_wait_for_signal()
287 {
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
341  break;
342  case WAIT_TIMEOUT:
343  RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
344  break;
345  case WAIT_FAILED:
346  RCLCPP_ERROR(
347  get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
348  break;
349  default:
350  RCLCPP_ERROR(
351  get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
352  GetLastError());
353  }
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 }
363 
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
382 }
383 
386 {
387  return signal_handlers_options_;
388 }
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:561