RSC  0.17.1
MacSignalWaiter.cpp
Go to the documentation of this file.
1 /* ============================================================
2  *
3  * This file is part of the RSC project
4  *
5  * Copyright (C) 2014 Johannes Wienke <jwienke@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 #include <boost/lexical_cast.hpp>
37 
38 #include "langutils.h"
39 
40 namespace rsc {
41 namespace misc {
42 
44 static std::string semaphore_name;
45 sem_t* semaphore;
46 volatile sig_atomic_t receivedSignal = 0;
47 
48 void handler(int signal) {
49  // Store the number of the received signal and post a token to the
50  // semaphore to allow the blocking sem_wait() call in
51  // waitForSignal() to complete.
52  receivedSignal = signal;
53  sem_post(semaphore);
54 }
55 
56 void throwRuntimeError(int errorNumber, const std::string& description) {
57  char buffer[256];
58  strerror_r(errorNumber, buffer, 256);
59  throw std::runtime_error(boost::str(boost::format("%1%: %2%")
60  % description % buffer));
61 }
62 
63 void initSignalWaiter(int signals) {
64  // SIGNALS should specify one or more signals.
66  & signals) == 0) {
67  throw std::logic_error("At least one signal has to be specified.");
68  }
69 
70  requestedSignals = signals;
71 
72  // Initialize the semaphore to a blocked state.
73  semaphore_name = (randAlnumStr(3)
74  + boost::lexical_cast<std::string>(currentTimeMicros()));
75  if (SEM_FAILED == (semaphore = sem_open(semaphore_name.c_str(), O_CREAT | O_EXCL, 0644, 0))) {
76  throwRuntimeError(errno, "Failed to initialize semaphore");
77  }
78 
79  // The arrival of any of the requested signals unblocks the
80  // semaphore.
81  //
82  // SA_RESETHAND causes the original handler to be restored after
83  // completion of our handler. This reactivates the default
84  // behavior of terminating the program for e.g. a second SIGINT
85  // signal.
86  struct sigaction action;
87  action.sa_handler = &handler;
88  if (sigemptyset(&action.sa_mask) != 0) {
89  throwRuntimeError(errno, "Failed to adjust signal mask");
90  }
91  action.sa_flags = SA_RESETHAND;
92  if ((INTERRUPT_REQUESTED & signals) != 0) {
93  if (sigaction(SIGINT, &action, 0) != 0) {
94  throwRuntimeError(errno, "Failed to install SIGINT handler");
95  }
96  }
97  if ((TERMINATE_REQUESTED & signals) != 0) {
98  if (sigaction(SIGTERM, &action, 0) != 0) {
99  throwRuntimeError(errno, "Failed to install SIGTERM handler");
100  }
101  }
102  if ((QUIT_REQUESTED & signals) != 0) {
103  if (sigaction(SIGQUIT, &action, 0) != 0) {
104  throwRuntimeError(errno, "Failed to install SIGQUIT handler");
105  }
106  }
107 }
108 
110 
111  if (receivedSignal == 0) {
112  return NO_SIGNAL;
113  } else if (receivedSignal == SIGINT) {
114  return INTERRUPT_REQUESTED;
115  } else if (receivedSignal == SIGTERM) {
116  return TERMINATE_REQUESTED;
117  } else if (receivedSignal == SIGQUIT) {
118  return QUIT_REQUESTED;
119  } else {
120  throw std::runtime_error(
121  boost::str(
122  boost::format("unexpected signal number %1%")
123  % (int) receivedSignal));
124  }
125 
126 }
127 
129  if (requestedSignals == 0) {
130  throw std::logic_error("initSignalWaiter has to be called before"
131  " waitForSignal.");
132  }
133 
134  // sem_wait can be interrupted by any signal. Since we only want
135  // to wait for the specified signals, we have to resume waiting in
136  // case of random unrelated interruptions.
137  int result;
138  while ((-1 == (result = sem_wait(semaphore))) && (errno == EINTR));
139  if (result == -1) {
140  throwRuntimeError(errno, "Failed to wait for signal");
141  }
142 
143  // Even though potentially two different signals could be handled
144  // in parallel, we need to destroy the semaphore on mac, living
145  // with the potential crash situation, in order to not fill up the
146  // persistent resources for named semaphores.
147  // TODO restore all handlers to default action to prevent this?
148  if (sem_unlink(semaphore_name.c_str()) == -1) {
149  throwRuntimeError(errno, "Could not unlink semaphore used for waiting");
150  }
151  if (sem_close(semaphore) == -1) {
152  throwRuntimeError(errno, "Could not close semaphore used for waiting");
153  }
154 
155  assert(receivedSignal != 0);
156  return mappedSignal();
157 }
158 
160  if (requestedSignals == 0) {
161  throw std::logic_error("initSignalWaiter has to be called before"
162  " hasSignalArrived.");
163  }
164 
165  return mappedSignal();
166 }
167 
169  switch (signal) {
170  case INTERRUPT_REQUESTED:
171  return 1;
172  case TERMINATE_REQUESTED:
173  return 1;
174  case QUIT_REQUESTED:
175  return 1;
176  case NO_SIGNAL:
177  default:
178  throw std::logic_error("Invalid signal number");
179  }
180 }
181 
182 }
183 }
void initSignalWaiter(int signals)
Prepare waiting for signals.
Signal waitForSignal()
Block until one of the signals specified in initSignalWaiter arrives, then return the signal...
std::string randAlnumStr(const std::string::size_type &length)
Generates a random alpha-numeric string with fixed length.
Definition: langutils.cpp:108
Signal mappedSignal()
Signal
Signals waitForSignal can wait for.
Definition: SignalWaiter.h:44
boost::uint64_t currentTimeMicros()
Returns the current system time as microseconds.
Definition: langutils.cpp:82
Artificial enum entry to indicate that no signal is meant at all.
Definition: SignalWaiter.h:49
static std::string semaphore_name
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)