3

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.

Community
  • 1
  • 1
dshepherd
  • 4,989
  • 4
  • 39
  • 46

2 Answers2

6

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().

Community
  • 1
  • 1
dshepherd
  • 4,989
  • 4
  • 39
  • 46
  • I tried using this, the exception gets thrown just fine, however, it is not always caught so the program terminates with an uncaught exception. Any ideas? See my [question](https://stackoverflow.com/questions/52539331/exception-not-caught-after-signal) – hr87 Sep 27 '18 at 14:29
1

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
damienfrancois
  • 52,978
  • 9
  • 96
  • 110
  • Hmm, that's definitely much simpler than my approach. It has a couple of downsides though: you have to wait for the outer loop to reach the next step (which could be minutes to hours in some cases) and you need the computation code to "know about" the signal handling. – dshepherd Oct 14 '13 at 18:48
  • 1
    Agreed. One upside though is that you always end in a consistent state, after a full iteration, which allows you restarting the computation easily if needed. – damienfrancois Oct 14 '13 at 18:57