ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
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  RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
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  RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
95  auto & instance = SignalHandler::get_global_signal_handler();
96  auto old_signal_handler = instance.get_old_signal_handler(signum);
97  if (
98  SIG_ERR != old_signal_handler && SIG_IGN != old_signal_handler &&
99  SIG_DFL != old_signal_handler)
100  {
101  old_signal_handler(signum);
102  }
103  instance.signal_handler_common();
104 }
105 #endif
106 
109 {
111 }
112 
115 {
116  static SignalHandler & signal_handler = *new SignalHandler();
117  return signal_handler;
118 }
119 
120 bool
122 {
123  std::lock_guard<std::mutex> lock(install_mutex_);
124  bool already_installed = installed_.exchange(true);
125  if (already_installed) {
126  return false;
127  }
128  if (signal_handler_options == SignalHandlerOptions::None) {
129  return true;
130  }
131  signal_handlers_options_ = signal_handler_options;
132  try {
133  setup_wait_for_signal();
134  signal_received_.store(false);
135 
136  SignalHandler::signal_handler_type handler_argument;
137 #if defined(RCLCPP_HAS_SIGACTION)
138  memset(&handler_argument, 0, sizeof(handler_argument));
139  sigemptyset(&handler_argument.sa_mask);
140  handler_argument.sa_sigaction = &this->signal_handler;
141  handler_argument.sa_flags = SA_SIGINFO;
142 #else
143  handler_argument = &this->signal_handler;
144 #endif
145  if (
146  signal_handler_options == SignalHandlerOptions::SigInt ||
147  signal_handler_options == SignalHandlerOptions::All)
148  {
149  old_sigint_handler_ = set_signal_handler(SIGINT, handler_argument);
150  }
151 
152  if (
153  signal_handler_options == SignalHandlerOptions::SigTerm ||
154  signal_handler_options == SignalHandlerOptions::All)
155  {
156  old_sigterm_handler_ = set_signal_handler(SIGTERM, handler_argument);
157  }
158 
159  signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
160  } catch (...) {
161  installed_.store(false);
162  throw;
163  }
164  RCLCPP_DEBUG(get_logger(), "signal handler installed");
165  return true;
166 }
167 
168 bool
170 {
171  std::lock_guard<std::mutex> lock(install_mutex_);
172  bool installed = installed_.exchange(false);
173  if (!installed) {
174  return false;
175  }
176  try {
177  // TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
178  // I think we need to assert that we're the current signal handler, and mitigate if not.
179  if (
180  SignalHandlerOptions::SigInt == signal_handlers_options_ ||
181  SignalHandlerOptions::All == signal_handlers_options_)
182  {
183  set_signal_handler(SIGINT, old_sigint_handler_);
184  }
185  if (
186  SignalHandlerOptions::SigTerm == signal_handlers_options_ ||
187  SignalHandlerOptions::All == signal_handlers_options_)
188  {
189  set_signal_handler(SIGTERM, old_sigterm_handler_);
190  }
191  signal_handlers_options_ = SignalHandlerOptions::None;
192  RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
193  notify_signal_handler();
194  if (signal_handler_thread_.joinable()) {
195  signal_handler_thread_.join();
196  }
197  teardown_wait_for_signal();
198  } catch (...) {
199  installed_.exchange(true);
200  throw;
201  }
202  RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
203  return true;
204 }
205 
206 bool
208 {
209  return installed_.load();
210 }
211 
212 SignalHandler::~SignalHandler()
213 {
214  try {
215  uninstall();
216  } catch (const std::exception & exc) {
217  RCLCPP_ERROR(
218  get_logger(),
219  "caught %s exception when uninstalling signal handlers in rclcpp::~SignalHandler: %s",
220  rmw::impl::cpp::demangle(exc).c_str(), exc.what());
221  } catch (...) {
222  RCLCPP_ERROR(
223  get_logger(),
224  "caught unknown exception when uninstalling signal handlers in rclcpp::~SignalHandler");
225  }
226 }
227 
228 SignalHandler::signal_handler_type
229 SignalHandler::get_old_signal_handler(int signum)
230 {
231  if (SIGINT == signum) {
232  return old_sigint_handler_;
233  } else if (SIGTERM == signum) {
234  return old_sigterm_handler_;
235  }
236 #if defined(RCLCPP_HAS_SIGACTION)
237  SignalHandler::signal_handler_type ret;
238  memset(&ret, 0, sizeof(ret));
239  sigemptyset(&ret.sa_mask);
240  ret.sa_handler = SIG_DFL;
241  return ret;
242 #else
243  return SIG_DFL;
244 #endif
245 }
246 
247 void
248 SignalHandler::signal_handler_common()
249 {
250  auto & instance = SignalHandler::get_global_signal_handler();
251  instance.signal_received_.store(true);
252  RCLCPP_DEBUG(
253  get_logger(),
254  "signal_handler(): notifying deferred signal handler");
255  instance.notify_signal_handler();
256 }
257 
258 void
259 SignalHandler::deferred_signal_handler()
260 {
261  while (true) {
262  if (signal_received_.exchange(false)) {
263  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
264  for (auto context_ptr : rclcpp::get_contexts()) {
265  if (context_ptr->get_init_options().shutdown_on_signal) {
266  RCLCPP_DEBUG(
267  get_logger(),
268  "deferred_signal_handler(): "
269  "shutting down rclcpp::Context @ %p, because it had shutdown_on_signal == true",
270  static_cast<void *>(context_ptr.get()));
271  context_ptr->shutdown("signal handler");
272  }
273  }
274  }
275  if (!is_installed()) {
276  RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
277  break;
278  }
279  RCLCPP_DEBUG(
280  get_logger(), "deferred_signal_handler(): waiting for SIGINT/SIGTERM or uninstall");
281  wait_for_signal();
282  RCLCPP_DEBUG(
283  get_logger(), "deferred_signal_handler(): woken up due to SIGINT/SIGTERM or uninstall");
284  }
285 }
286 
287 void
288 SignalHandler::setup_wait_for_signal()
289 {
290 #if defined(_WIN32)
291  signal_handler_sem_ = CreateSemaphore(
292  NULL, // default security attributes
293  0, // initial semaphore count
294  1, // maximum semaphore count
295  NULL); // unnamed semaphore
296  if (NULL == signal_handler_sem_) {
297  throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
298  }
299 #elif defined(__APPLE__)
300  signal_handler_sem_ = dispatch_semaphore_create(0);
301 #else // posix
302  if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
303  throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
304  }
305 #endif
306  wait_for_signal_is_setup_.store(true);
307 }
308 
309 void
310 SignalHandler::teardown_wait_for_signal() noexcept
311 {
312  if (!wait_for_signal_is_setup_.exchange(false)) {
313  return;
314  }
315 #if defined(_WIN32)
316  CloseHandle(signal_handler_sem_);
317 #elif defined(__APPLE__)
318  dispatch_release(signal_handler_sem_);
319 #else // posix
320  if (-1 == sem_destroy(&signal_handler_sem_)) {
321  RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
322  }
323 #endif
324 }
325 
326 void
327 SignalHandler::wait_for_signal()
328 {
329  if (!wait_for_signal_is_setup_.load()) {
330  RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
331  return;
332  }
333 #if defined(_WIN32)
334  DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
335  switch (dw_wait_result) {
336  case WAIT_ABANDONED:
337  RCLCPP_ERROR(
338  get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
339  GetLastError());
340  break;
341  case WAIT_OBJECT_0:
342  // successful
343  break;
344  case WAIT_TIMEOUT:
345  RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
346  break;
347  case WAIT_FAILED:
348  RCLCPP_ERROR(
349  get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
350  break;
351  default:
352  RCLCPP_ERROR(
353  get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
354  GetLastError());
355  }
356 #elif defined(__APPLE__)
357  dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
358 #else // posix
359  int s;
360  do {
361  s = sem_wait(&signal_handler_sem_);
362  } while (-1 == s && EINTR == errno);
363 #endif
364 }
365 
366 void
367 SignalHandler::notify_signal_handler() noexcept
368 {
369  if (!wait_for_signal_is_setup_.load()) {
370  return;
371  }
372 #if defined(_WIN32)
373  if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
374  RCLCPP_ERROR(
375  get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
376  }
377 #elif defined(__APPLE__)
378  dispatch_semaphore_signal(signal_handler_sem_);
379 #else // posix
380  if (-1 == sem_post(&signal_handler_sem_)) {
381  RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
382  }
383 #endif
384 }
385 
388 {
389  return signal_handlers_options_;
390 }
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:535