Tomographer  v5.4
Tomographer C++ Framework Documentation
signal_handler.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2016 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  * Copyright (c) 2017 Caltech, Institute for Quantum Information and Matter, Philippe Faist
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 
29 #ifndef TOMOGRAPHER_TOOLS_SIGNAL_HANDLER
30 #define TOMOGRAPHER_TOOLS_SIGNAL_HANDLER
31 
32 
39 #include <signal.h>
40 #include <stdio.h>
41 
42 #include <cstdio>
43 #include <ctime>
44 
45 #include <chrono>
46 
47 
48 namespace Tomographer
49 {
50 namespace Tools
51 {
52 
58 struct TOMOGRAPHER_EXPORT SignalHandler
59 {
60  SignalHandler() { }
61  virtual ~SignalHandler() { }
62 
67  virtual void handleSignal(int sig_num) = 0;
68 };
69 
70 
71 
72 #ifndef TOMOGRAPHER_SIG_HANDLER_REPEAT_EXIT_DELAY
73 #define TOMOGRAPHER_SIG_HANDLER_REPEAT_EXIT_DELAY 2
74 #endif
75 
76 
77 namespace tomo_internal {
78 
79  //
80  // helper for formatting a string inside a signal handler, using only
81  // signal-safe POSIX C functions
82  //
83  static inline void format_string_with_signal_num(char * buffer, const std::size_t buffer_max_len,
84  int signum, const char * msgstart, const char * msgend)
85  {
86  const std::size_t msgstartlen = strlen(msgstart);
87  const std::size_t msgendlen = strlen(msgend);
88 
89  // silence "unused" warnings if building without asserts
90  (void)buffer_max_len;
91  (void)msgendlen;
92 
93  assert(signum < 100) ;
94  assert(buffer_max_len >= msgstartlen + 2 + msgendlen + 1) ;
95 
96  // strcpy() and strcat() are signal-safe according to POSIX requirements
97 
98  strcpy(buffer, msgstart);
99 
100  buffer[msgstartlen] = (48 + (signum/10)%10); // 48 == '0'
101  buffer[msgstartlen+1] = 48 + (signum%10);
102  buffer[msgstartlen+2] = '\0';
103 
104  strcat(buffer, msgend);
105  }
106 
107  static inline void write_string_stderr_ignore_result(const char * msg) // zero-terminated
108  {
109 #pragma GCC diagnostic push
110 #pragma GCC diagnostic ignored "-Wunused-result"
111 
112  write(STDERR_FILENO, msg, strlen(msg));
113 
114 #pragma GCC diagnostic pop
115  }
116 
117  static std::time_t last_sig_hit_time[NSIG] = { 0 };
118  static SignalHandler * signal_handler[NSIG] = { NULL };
119 
120  //
121  // hmmm.... actually our implementation is not safe strictly speaking, because
122  // we call functions like std::fprintf which are not signal-reentrant -- see
123  // http://en.cppreference.com/w/cpp/utility/program/signal#Signal_handler
124  //
125  static void signal_dispatch_fn(int signum)
126  {
127  //
128  // POSIX defines a set of functions which we can call from a signal handler --
129  // http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03_03
130  //
131 
132  using namespace std;
133 
134  // abort() is OK in a signal handler
135  assert(signum >= 0 && signum < NSIG && "signum out of range 0...NSIG !") ;
136 
137 #if defined(__MINGW32__) || defined(__MINGW64__)
138  // re-attach handler---seems like it's needed on Windows...
139  // signal() is OK in a signal handler
140  signal(signum, tomo_internal::signal_dispatch_fn);
141 #endif
142 
143  // write() is OK in a signal handler, fprintf() is not. So do the formatting ourselves.
144  //fprintf(stderr, "\n*** interrupt (%d)\n", signum);
145  // strcpy() and strlen() are OK
146  { const char * msgint1 = "\n*** interrupt (";
147  const char * msgint2 = ")\n";
148  char msgint[32];
149  format_string_with_signal_num(msgint, sizeof(msgint), signum, msgint1, msgint2);
150  write_string_stderr_ignore_result(msgint);
151  }
152 
153  time_t now;
154 
155  // time() is OK in a signal handler
156  time(&now);
157 
158  if ( (now - tomo_internal::last_sig_hit_time[signum]) < TOMOGRAPHER_SIG_HANDLER_REPEAT_EXIT_DELAY ) {
159  // two interrupts within two seconds --> exit
160  // write() is OK in a signal handler, fprintf() is not
161  //fprintf(stderr, "\n*** Exit\n");
162  const char * bufferexit = "\n*** Exit\n";
163  write_string_stderr_ignore_result(bufferexit);
164  ::exit(1);
165  return;
166  }
167 
168  tomo_internal::last_sig_hit_time[signum] = now;
169 
170  if (signal_handler[signum] != NULL) {
171  signal_handler[signum]->handleSignal(signum);
172  } else {
173  // write() is OK in a signal handler, fprintf() is not. So do the formatting ourselves.
174  const char * msghandle1 = "Warning: sig_handle: no signal handler set (got signal ";
175  const char * msghandle2 = ")\n";
176  char msghandle[1024];
177  format_string_with_signal_num(msghandle, sizeof(msghandle), signum, msghandle1, msghandle2) ;
178  write_string_stderr_ignore_result(msghandle);
179  }
180 
181  }
182 }
183 
184 
190 inline void installSignalHandler(int signum, SignalHandler * sobj)
191 {
192  tomo_internal::signal_handler[signum] = sobj;
193  signal(signum, tomo_internal::signal_dispatch_fn);
194 }
195 
196 
197 } // namespace Tools
198 } // namespace Tomographer
199 
200 
201 #endif
Base namespace for the Tomographer project.
Definition: densellh.h:45
STL namespace.
T time(T... args)
An abstract signal handler (C++ interface)
void installSignalHandler(int signum, SignalHandler *sobj)
Installs the given signal handler to catch the signal signum.
virtual void handleSignal(int sig_num)=0
Perform some action in reaction to a signal.
T signal(T... args)