RSC  0.19.0
PosixSignalWaiter.cpp
Go to the documentation of this file.
1 /* ============================================================
2  *
3  * This file is part of the RSC project
4  *
5  * Copyright (C) 2014, 2017 Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
6  *
7  * This file may be licensed under the terms of the
8  * GNU Lesser General Public License Version 3 (the ``LGPL''),
9  * or (at your option) any later version.
10  *
11  * Software distributed under the License is distributed
12  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
13  * express or implied. See the LGPL for the specific language
14  * governing rights and limitations.
15  *
16  * You should have received a copy of the LGPL along with this
17  * program. If not, go to http://www.gnu.org/licenses/lgpl.html
18  * or write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * The development of this software was supported by:
22  * CoR-Lab, Research Institute for Cognition and Robotics
23  * Bielefeld University
24  *
25  * ============================================================ */
26 
27 #include "SignalWaiter.h"
28 
29 #include <errno.h>
30 #include <string.h>
31 
32 #include <signal.h>
33 #include <semaphore.h>
34 
35 #include <boost/format.hpp>
36 
37 namespace rsc {
38 namespace misc {
39 
40 int requestedSignals = 0;
41 sem_t semaphore;
42 volatile sig_atomic_t receivedSignal = 0;
43 
44 void handler(int signal) {
45  // Store the number of the received signal and post a token to the
46  // semaphore to allow the blocking sem_wait() call in
47  // waitForSignal() to complete.
48  receivedSignal = signal;
49  sem_post(&semaphore);
50 }
51 
52 void throwRuntimeError(int errorNumber, const std::string& description) {
53  char buffer[256];
54  if (strerror_r(errorNumber, buffer, 256) != 0) {
55  throw std::runtime_error(boost::str(boost::format("Error %1% (could not determine error message: %2%)")
56  % errorNumber % errno));
57  }
58  throw std::runtime_error(boost::str(boost::format("%1%: %2%")
59  % description % buffer));
60 }
61 
62 void initSignalWaiter(int signals) {
63  // SIGNALS should specify one or more signals.
65  & signals) == 0) {
66  throw std::logic_error("At least one signal has to be specified.");
67  }
68  requestedSignals = signals;
69 
70  // Initialize the semaphore to a blocked state.
71  if (sem_init(&semaphore, 0, 0) != 0) {
72  throwRuntimeError(errno, "Failed to initialize semaphore");
73  }
74 
75  // The arrival of any of the requested signals unblocks the
76  // semaphore.
77  //
78  // SA_RESETHAND causes the original handler to be restored after
79  // completion of our handler. This reactivates the default
80  // behavior of terminating the program for e.g. a second SIGINT
81  // signal.
82  struct sigaction action;
83  action.sa_handler = &handler;
84  if (sigemptyset(&action.sa_mask) != 0) {
85  throwRuntimeError(errno, "Failed to adjust signal mask");
86  }
87  action.sa_flags = SA_RESETHAND;
88  if ((INTERRUPT_REQUESTED & signals) != 0) {
89  if (sigaction(SIGINT, &action, 0) != 0) {
90  throwRuntimeError(errno, "Failed to install SIGINT handler");
91  }
92  }
93  if ((TERMINATE_REQUESTED & signals) != 0) {
94  if (sigaction(SIGTERM, &action, 0) != 0) {
95  throwRuntimeError(errno, "Failed to install SIGTERM handler");
96  }
97  }
98  if ((QUIT_REQUESTED & signals) != 0) {
99  if (sigaction(SIGQUIT, &action, 0) != 0) {
100  throwRuntimeError(errno, "Failed to install SIGQUIT handler");
101  }
102  }
103 }
104 
106 
107  if (receivedSignal == 0) {
108  return NO_SIGNAL;
109  } else if (receivedSignal == SIGINT) {
110  return INTERRUPT_REQUESTED;
111  } else if (receivedSignal == SIGTERM) {
112  return TERMINATE_REQUESTED;
113  } else if (receivedSignal == SIGQUIT) {
114  return QUIT_REQUESTED;
115  } else {
116  throw std::runtime_error(
117  boost::str(
118  boost::format("unexpected signal number %1%")
119  % (int)receivedSignal));
120  }
121 
122 }
123 
125  if (requestedSignals == 0) {
126  throw std::logic_error("initSignalWaiter has to be called before"
127  " waitForSignal.");
128  }
129 
130  // sem_wait can be interrupted by any signal. Since we only want
131  // to wait for the specified signals, we have to resume waiting in
132  // case of random unrelated interruptions.
133  int result;
134  while ((-1 == (result = sem_wait(&semaphore))) && (errno == EINTR));
135  if (result == -1) {
136  throwRuntimeError(errno, "Failed to wait for signal");
137  }
138 
139  // We do not delete this semaphore because the respective "other"
140  // handlers may still run (i.e. we receive SIGINT followed by
141  // SIGTERM, for example) and access the semaphore.
142  // sem_destroy(&semaphore);
143 
144  assert(receivedSignal != 0);
145  return mappedSignal();
146 }
147 
149  if (requestedSignals == 0) {
150  throw std::logic_error("initSignalWaiter has to be called before"
151  " hasSignalArrived.");
152  }
153 
154  return mappedSignal();
155 }
156 
157 int suggestedExitCode(Signal signal) {
158  switch (signal) {
159  case INTERRUPT_REQUESTED:
160  return 1;
161  case TERMINATE_REQUESTED:
162  return 1;
163  case QUIT_REQUESTED:
164  return 1;
165  case NO_SIGNAL:
166  default:
167  throw std::logic_error("Invalid signal number");
168  }
169 }
170 
171 }
172 }
void initSignalWaiter(int signals)
Prepare waiting for signals.
Signal waitForSignal()
Block until one of the signals specified in initSignalWaiter arrives, then return the signal...
Signal mappedSignal()
Signal
Signals waitForSignal can wait for.
Definition: SignalWaiter.h:44
Artificial enum entry to indicate that no signal is meant at all.
Definition: SignalWaiter.h:49
Signal lastArrivedSignal()
Returns the last signal that has arrived at this process or Signal::NO_SIGNAL in case no signal arriv...
Interrupting the program has be requested.
Definition: SignalWaiter.h:56
sem_t * semaphore
int suggestedExitCode(Signal signal)
Return suggested exit code for exiting the program after receiving signal signal. ...
Quitting the program has be requested.
Definition: SignalWaiter.h:70
void throwRuntimeError(int errorNumber, const std::string &description)
volatile sig_atomic_t receivedSignal
Terminating the program has be requested.
Definition: SignalWaiter.h:63
void handler(int signal)