Skip to content

Instantly share code, notes, and snippets.

@CristianCantoro
Last active February 28, 2018 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CristianCantoro/c7a8cc7f03c6ccb7e270b45d9f6da712 to your computer and use it in GitHub Desktop.
Save CristianCantoro/c7a8cc7f03c6ccb7e270b45d9f6da712 to your computer and use it in GitHub Desktop.
# ignore executables
test_signal
test_setitimer
# ignore input and output files
input.txt
output.txt
#include <chrono>
#include <iostream>
#include <unistd.h>
#include <csignal>
#include <sys/time.h>
#include <sys/resource.h>
#include "test_itimer.h"
using namespace std;
/*
* Very simple debugger, enabled at compile time with -DDEBUG.
* If enabled, it prints on stderr, otherwise it does nothing (it does not
* even evaluate the expression on its right-hand side).
*
* Main ideas taken from:
* - C++ enable/disable debug messages of std::couts on the fly
* (https://stackoverflow.com/q/3371540/2377454)
* - Standard no-op output stream
* (https://stackoverflow.com/a/11826787/2377454)
*/
#ifdef DEBUG
#define debug true
#else
#define debug false
#endif
#define debug_logger if (!debug) \
{} \
else \
cerr << "[DEBUG] helpers::"
// conversion factor betwen seconds and nanoseconds
#define NANOS 1000000000
// signal to handle
#define SIGNAL SIGVTALRM
#define TIMELIMIT ITIMER_VIRTUAL
namespace helpers {
long long start_time = -1;
volatile sig_atomic_t timeout_flag = false;
unsigned const timelimit = 2; // soft limit on CPU time (in seconds)
void setup_signal(void);
void setup_time_limit(void);
static void signal_handler(int signum);
}
/*
* This could be a function factory where and a closure of the signal-handling
* function so that we could explicitly pass the output ofstream and close it.
* C++ support closures only for lambdas, alas, at the moment we also need
* the signal-handling function to be a pointer to a function and lambaa are
* a different object that can not be converted. See:
* - Passing lambda as function pointer
* (https://stackoverflow.com/a/28746827/2377454)
*/
void helpers::signal_handler(int signum) {
helpers::timeout_flag = true;
debug_logger << "signal_handler:\t" << "signal " << signum \
<< " received" << endl;
debug_logger << "signal_handler:\t" << "exiting after " \
<< helpers::get_elapsed_time() << " microseconds" << endl;
exit(0);
}
/*
* Set function signal_handler() as handler for SIGXCPU using sigaction. See
* - https://stackoverflow.com/q/4863420/2377454
* - https://stackoverflow.com/a/17572787/2377454
*/
void helpers::setup_signal() {
debug_logger << "set_signal:\t" << "set_signal() called" << endl;
struct sigaction new_action;
//Set the handler in the new_action struct
new_action.sa_handler = signal_handler;
// Set to empty the sa_mask. It means that no signal is blocked
// while the handler run.
sigemptyset(&new_action.sa_mask);
// Block the SIGXCPU signal, while the handler run, SIGXCPU is ignored.
sigaddset(&new_action.sa_mask, SIGNAL);
// Remove any flag from sa_flag
new_action.sa_flags = 0;
// Set new action
sigaction(SIGNAL, &new_action, NULL);
if(debug) {
struct sigaction tmp;
// read the old signal associated to SIGXCPU
sigaction(SIGNAL, NULL, &tmp);
debug_logger << "set_signal:\t" << "action.sa_handler: " \
<< tmp.sa_handler << endl;
}
return;
}
/*
* Set soft CPU time limit.
* RLIMIT_CPU set teg CPU time limit in seconds..
* See:
* - https://www.go4expert.com/articles/
* getrlimit-setrlimit-control-resources-t27477/
* - https://gist.github.com/Leporacanthicus/11086960
*/
void helpers::setup_time_limit(void) {
debug_logger << "set_limit:\t\t" << "set_limit() called" << endl;
struct itimerval old_limit, new_limit;
if (getitimer(TIMELIMIT, &old_limit) != 0) {
perror("error calling getitimer()");
exit(EXIT_FAILURE);
}
debug_logger << "set_limit:\t\t" << "old_limit.it_value.tv_sec: " \
<< old_limit.it_value.tv_sec << endl;
debug_logger << "set_limit:\t\t" << "old_limit.it_value.tv_usec: " \
<< old_limit.it_value.tv_usec << endl;
new_limit.it_value.tv_sec = helpers::timelimit;
new_limit.it_value.tv_usec = 0;
new_limit.it_interval.tv_sec = helpers::timelimit;
new_limit.it_interval.tv_usec = 0;
if (setitimer(TIMELIMIT, &new_limit, &old_limit) != 0) {
perror("error calling setitimer()");
exit(EXIT_FAILURE);
}
if (debug) {
struct itimerval tmp;
getitimer(TIMELIMIT, &tmp);
debug_logger << "set_limit:\t\t" << "current limit:" << endl;
debug_logger << "set_limit:\t\t" << "tmp.it_value.tv_sec: " \
<< tmp.it_value.tv_sec << endl;
debug_logger << "set_limit:\t\t" << "tmp.it_value.tv_usec: " \
<< tmp.it_value.tv_usec << endl;
debug_logger << "set_limit:\t\t" << "tmp.it_interval.tv_sec: " \
<< tmp.it_interval.tv_sec << endl;
debug_logger << "set_limit:\t\t" << "tmp.it_interval.tv_usec: " \
<< tmp.it_interval.tv_usec << endl;
}
return;
}
void helpers::setup(void) {
struct timespec start;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start)) {
exit(EXIT_FAILURE);
}
start_time = start.tv_sec*NANOS + start.tv_nsec;
setup_signal();
setup_time_limit();
return;
}
long long helpers::get_elapsed_time(void) {
struct timespec current;
if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &current)) {
exit(EXIT_FAILURE);
}
long long current_time = current.tv_sec*NANOS + current.tv_nsec;
long long elapsed_micro = (current_time - start_time)/1000 + \
((current_time - start_time) % 1000 >= 500);
return elapsed_micro;
}
bool helpers::has_reached_timeout(void) {
return helpers::timeout_flag;
}
#ifndef HELPERS_HPP
#define HELPERS_HPP
using namespace std;
namespace helpers {
long long get_elapsed_time(void);
bool has_reached_timeout(void);
void setup(void);
}
#endif
#pragma once
/*
* Test program for signal handling on CMS.
*
* Compile with:
* /usr/bin/g++ [-DDEBUG] -Wall -std=c++11 -O2 -pipe -static -s \
* -o test_signal test_signal.cpp helpers.cpp
*
* The option -DDEBUG activates some debug logging in the helpers library.
*/
#include <iostream>
#include <fstream>
#include <random>
#include "test_itimer.h"
using namespace std;
namespace {
unsigned const minrand = 5;
unsigned const maxrand = 20;
int const numcycles = 5000000;
};
int main() {
helpers::setup();
ifstream in("input.txt");
in.close();
ofstream out("output.txt");
random_device rd;
mt19937 eng(rd());
uniform_int_distribution<> distr(minrand, maxrand);
int i = 0;
while(!helpers::has_reached_timeout()) {
int nmsec;
for(int n=0; n<numcycles; n++) {
nmsec = distr(eng);
}
/*
for (int i=0; i<10; i++) {
for(int n=0; n<250000; n++) {
cout << "x" << '\b';
}
cout << '.';
}
cout << "->" << '\t';
*/
cout << "i: " << i << "\t- nmsec: " << nmsec << "\t- ";
out << "i: " << i << "\t- nmsec: " << nmsec << "\t- ";
cout << "program has been running for " << \
helpers::get_elapsed_time() << " microseconds" << endl;
out << "program has been running for " << \
helpers::get_elapsed_time() << " microseconds" << endl;
i++;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment