I want to write a few extra lines to a file when interrupted with ctrl-c before the program dies. However the location of the file is not hard coded so I need something more than normal interrupt handling as explained here. What is the best way to do this?
Motivation:
I'm doing time dependent finite element simulations. Sometimes I forget or mis-estimate a reasonable tmax and the simulation would take a very long time to complete, so I interrupt it with ctrl-c. However I still want to use the data generated up to that point. In particular there's an xml file which needs a few lines appended after the simulation is finished to close the tags. Of course it's possible to do this by hand but it's a pain, so I'm try to automate it.
Based on the answer to "How can I catch a ctrl-c event? (C++)" we can install a signal handler that simply raises an exception. Then we can catch the exception and run whatever standard c++ code we like.
Here's a working example:
#include <iostream>
#include <exception>
#include <signal.h>
#include <stdlib.h>
class InterruptException : public std::exception
{
public:
InterruptException(int s) : S(s) {}
int S;
};
void sig_to_exception(int s)
{
throw InterruptException(s);
}
int main()
{
// Taken from answer to "How can I catch a ctrl-c event? (C++)"
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = sig_to_exception;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
try
{
std::cout << "Inside try" << std::endl;
// Do something with output so the loop doesn't get optimised out
double i = 0;
while(i < 1e30) {i++;} // loop until interrupted
std::cout << i << std::endl;
}
catch(InterruptException& e)
{
std::cout << "Write something to file" << std::endl;
std::cout << "Caught signal " << e.S << std::endl;
return 1;
}
return 0;
}
This approach does have at least one downside: we have to wrap everything after the handler is installed in a try, otherwise interrupt signals would cause an abort().
One common scheme for loop-based scientific code is to have a global volatile sig_atomic_t boolean indicating whether a signal was caught and add it to the loop condition.
e.g.
volatile sig_atomic_t interrupted=false;
...
void signal_handler(int s)
{
// ...
interrupted=true;
}
...
while (!converged && !interrupted)
{
// Perform computations
}
// Save file properly