Browse Source

Signal handler supporting termination after waiting period

main
Matthias Volk 5 years ago
parent
commit
45aa451be5
  1. 3
      src/storm-cli-utilities/cli.cpp
  2. 150
      src/storm/utility/resources.h

3
src/storm-cli-utilities/cli.cpp

@ -204,6 +204,9 @@ namespace storm {
if (resources.isTimeoutSet()) {
storm::utility::resources::setCPULimit(resources.getTimeoutInSeconds());
}
// register signal handler to handle aborts
storm::utility::resources::installSignalHandler();
}
void setLogLevel() {

150
src/storm/utility/resources.h

@ -8,36 +8,70 @@
#include <sys/resource.h>
#include "storm-config.h"
#include "storm/utility/OsDetection.h"
#include "storm/utility/macros.h"
namespace storm {
namespace utility {
namespace resources {
static const int STORM_EXIT_GENERALERROR = -1;
static const int STORM_EXIT_TIMEOUT = -2;
static const int STORM_EXIT_MEMOUT = -3;
// Maximal waiting time after abort signal before terminating
static const int maxWaitTime = 3;
// Flag whether the program should terminate
static bool terminate = false;
// Store last signal code
static int lastSignal = 0;
/*!
* Check whether the program should terminate (due to some abort signal).
* @return True iff program should terminate.
*/
inline bool isTerminate() {
return terminate;
}
/*! Get the error code.
* Default is 0. If a signal was handled, the corresponding signal code is returned.
* @return Error code.
*/
inline int getErrorCode() {
return lastSignal;
}
/*!
* Get CPU limit.
* @return CPU limit in seconds.
*/
inline std::size_t getCPULimit() {
rlimit rl;
getrlimit(RLIMIT_CPU, &rl);
return rl.rlim_cur;
}
/*!
* Set CPU limit.
* @param seconds CPU limit in seconds.
*/
inline void setCPULimit(std::size_t seconds) {
rlimit rl;
getrlimit(RLIMIT_CPU, &rl);
rl.rlim_cur = seconds;
setrlimit(RLIMIT_CPU, &rl);
}
/*!
* Get used CPU time.
* @return CPU time in seconds.
*/
inline std::size_t usedCPU() {
return std::size_t(clock()) / CLOCKS_PER_SEC;
}
/*!
* Get memory limit.
* @return Memory limit in MB.
*/
inline std::size_t getMemoryLimit() {
#if defined LINUX
rlimit rl;
@ -48,7 +82,11 @@ namespace storm {
return 0;
#endif
}
/*!
* Set memory limit.
* @param megabytes Memory limit in MB.
*/
inline void setMemoryLimit(std::size_t megabytes) {
#if defined LINUX
rlimit rl;
@ -56,11 +94,15 @@ namespace storm {
rl.rlim_cur = megabytes * 1024 * 1024;
setrlimit(RLIMIT_AS, &rl);
#else
(void)megabytes;
(void) megabytes; // To silence "unused" warning
STORM_LOG_WARN("Setting a memory limit is not supported for your operating system.");
#endif
}
/*!
* Exit without cleanup.
* @param errorCode Error code to return.
*/
inline void quickest_exit(int errorCode) {
#if defined LINUX
std::quick_exit(errorCode);
@ -70,25 +112,83 @@ namespace storm {
std::abort();
#endif
}
/*!
* Set timeout by raising an alarm after timeout seconds.
* @param timeout Timeout in seconds.
*/
inline void setTimeoutAlarm(uint_fast64_t timeout) {
alarm(timeout);
}
/*!
* Signal handler for aborts, etc.
* After the first signal the handler waits a number of seconds to let the program print preliminary results
* which were computed up this point. If the waiting time is exceeded or if a second signal was raised
* in the mean time, the program immediately terminates.
* @param signal Exit code of signal.
*/
inline void signalHandler(int signal) {
if (signal == SIGXCPU) {
std::cerr << "Timeout." << std::endl;
quickest_exit(STORM_EXIT_TIMEOUT);
} else if (signal == ENOMEM) {
std::cerr << "Out of memory" << std::endl;
quickest_exit(STORM_EXIT_MEMOUT);
if (!isTerminate()) {
// First time we get an abort signal
// We give the program a number of seconds to print results obtained so far before termination
std::cerr << "ERROR: The program received signal " << signal << " and will be aborted in " << maxWaitTime << "s." << std::endl;
terminate = true;
// Remember original signal such that the program returns the correct original signal
lastSignal = signal;
// Trigger a new signal after waitTime
setTimeoutAlarm(maxWaitTime);
} else {
std::cerr << "Unknown abort in resource limitation module." << std::endl;
quickest_exit(STORM_EXIT_GENERALERROR);
// Second time we get a signal
// We now definitely have to terminate as fast as possible
if (getErrorCode() == SIGXCPU) {
std::cerr << "TIMEOUT." << std::endl;
} else if (getErrorCode() == ENOMEM) {
std::cerr << "OUT OF MEMORY." << std::endl;
} else if (getErrorCode() == SIGABRT || getErrorCode() == SIGINT) {
std::cerr << "ABORT." << std::endl;
} else {
std::cerr << "Received signal " << signal << std::endl;
}
quickest_exit(getErrorCode());
}
}
/*!
* Register some signal handlers to detect and correctly handle abortion (due to timeout for example).
*/
inline void installSignalHandler() {
std::signal(SIGXCPU, signalHandler);
std::signal(ENOMEM, signalHandler);
// We use the newer sigaction instead of signal
struct sigaction sa;
sa.sa_handler = signalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
// CPU Limit
if (sigaction(SIGXCPU, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
// Memory out
if (sigaction(ENOMEM, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
if (sigaction(SIGSEGV, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
if (sigaction(SIGABRT, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
if (sigaction(SIGINT, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
if (sigaction(SIGTERM, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
if (sigaction(SIGALRM, &sa, nullptr) == -1) {
std::cerr << "FATAL: Installing a signal handler failed." << std::endl;
}
}
}
}
}

Loading…
Cancel
Save