RSC  0.16.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 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  strerror_r(errorNumber, buffer, 256);
55  throw std::runtime_error(boost::str(boost::format("%1%: %2%")
56  % description % buffer));
57 }
58 
59 void initSignalWaiter(int signals) {
60  // SIGNALS should specify one or more signals.
62  & signals) == 0) {
63  throw std::logic_error("At least one signal has to be specified.");
64  }
65  requestedSignals = signals;
66 
67  // Initialize the semaphore to a blocked state.
68  if (sem_init(&semaphore, 0, 0) != 0) {
69  throwRuntimeError(errno, "Failed to initialize semaphore");
70  }
71 
72  // The arrival of any of the requested signals unblocks the
73  // semaphore.
74  //
75  // SA_RESETHAND causes the original handler to be restored after
76  // completion of our handler. This reactivates the default
77  // behavior of terminating the program for e.g. a second SIGINT
78  // signal.
79  struct sigaction action;
80  action.sa_handler = &handler;
81  if (sigemptyset(&action.sa_mask) != 0) {
82  throwRuntimeError(errno, "Failed to adjust signal mask");
83  }
84  action.sa_flags = SA_RESETHAND;
85  if ((INTERRUPT_REQUESTED & signals) != 0) {
86  if (sigaction(SIGINT, &action, 0) != 0) {
87  throwRuntimeError(errno, "Failed to install SIGINT handler");
88  }
89  }
90  if ((TERMINATE_REQUESTED & signals) != 0) {
91  if (sigaction(SIGTERM, &action, 0) != 0) {
92  throwRuntimeError(errno, "Failed to install SIGTERM handler");
93  }
94  }
95  if ((QUIT_REQUESTED & signals) != 0) {
96  if (sigaction(SIGQUIT, &action, 0) != 0) {
97  throwRuntimeError(errno, "Failed to install SIGQUIT handler");
98  }
99  }
100 }
101 
103 
104  if (receivedSignal == 0) {
105  return NO_SIGNAL;
106  } else if (receivedSignal == SIGINT) {
107  return INTERRUPT_REQUESTED;
108  } else if (receivedSignal == SIGTERM) {
109  return TERMINATE_REQUESTED;
110  } else if (receivedSignal == SIGQUIT) {
111  return QUIT_REQUESTED;
112  } else {
113  throw std::runtime_error(
114  boost::str(
115  boost::format("unexpected signal number %1%")
116  % (int)receivedSignal));
117  }
118 
119 }
120 
122  if (requestedSignals == 0) {
123  throw std::logic_error("initSignalWaiter has to be called before"
124  " waitForSignal.");
125  }
126 
127  // sem_wait can be interrupted by any signal. Since we only want
128  // to wait for the specified signals, we have to resume waiting in
129  // case of random unrelated interruptions.
130  int result;
131  while ((-1 == (result = sem_wait(&semaphore))) && (errno == EINTR));
132  if (result == -1) {
133  throwRuntimeError(errno, "Failed to wait for signal");
134  }
135 
136  // We do not delete this semaphore because the respective "other"
137  // handlers may still run (i.e. we receive SIGINT followed by
138  // SIGTERM, for example) and access the semaphore.
139  // sem_destroy(&semaphore);
140 
141  assert(receivedSignal != 0);
142  return mappedSignal();
143 }
144 
146  if (requestedSignals == 0) {
147  throw std::logic_error("initSignalWaiter has to be called before"
148  " hasSignalArrived.");
149  }
150 
151  return mappedSignal();
152 }
153 
154 int suggestedExitCode(Signal signal) {
155  switch (signal) {
156  case INTERRUPT_REQUESTED:
157  return 1;
158  case TERMINATE_REQUESTED:
159  return 1;
160  case QUIT_REQUESTED:
161  return 1;
162  case NO_SIGNAL:
163  default:
164  throw std::logic_error("Invalid signal number");
165  }
166 }
167 
168 }
169 }
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)