1

I wonder if there is a good way to terminate my process written in C++11 after a while?

In my process I have a main class with a pure virtual function run() running the main program that could be blocked in communication processes. I want my run() function to be forced to finish after a while (even if blocked) and the destructor of my main class (and all the destructors of the members) to be called.

Now I have a timer that call std::terminate via a callback.

namespace Timer
{
    void start(Duration time, function<void()> task)
    {
        thread([time, task]() {
            this_thread::sleep_for(time);
            task();
        }).detach();
    }
}
risingDarkness
  • 698
  • 12
  • 25
didil
  • 693
  • 8
  • 22
  • 1
    If the `run()` function will not return, you cannot end the program cleanly. Maybe you could run `run()` in a thread, and call `thread::detach()` on it: main destructors will be called, but I wouldn't call it _clean_. – rodrigo Nov 10 '15 at 08:48
  • 1
    I had this issue, too with a program that could be stuck in communication. I have added a heartbeat meachanism to it, so I know every X seconds (in my case every 45 seconds) a message will be received and sent and then put in a timeout of a minute. So I never get in a case where I am stuck and waiting for a message. Of course the worst case was that the program needed 45 seconds to shut down, but did it clean! – Nidhoegger Nov 10 '15 at 08:50
  • If a (non-detached) thread is blocked it can't be gracefully terminated without first unblocking it. You must control this syntactically by, _e.g._, using wait/notify or periodically check status using `wait_for` or similar. – Felix Glas Nov 10 '15 at 08:52
  • I cannot unblock extra libraries and I don't want to wait the communication timeout (this is not related to communication issues). I simply want to force the program to finish after a while to be able to stop properly the process (calling all the destructors) in the time. If the main program finished before the timer, it should also terminate properly (the run time can be shorter than the timer but never longer). – didil Nov 10 '15 at 09:21
  • 1
    Wrap `run` in a thread. If it doesn't finish in time, just do a normal exit. You will get everything above `run` shut down properly. You don't control things below `run`, there's no hope of making them exit gracefully anyway. – n. m. could be an AI Nov 10 '15 at 11:02
  • This is the solution I will keep. – didil Nov 10 '15 at 11:05

3 Answers3

2

The real solution would be to deal with the cause and not the symptom:

  • symptom: run function never ends
  • cause: a communication request never ends

Most communication (input) functions are interruptible, or have native timeouts. If your communication routines have no native timeouts, you could (maybe) wrap them in a way using an alarm Posix call that should cleanly interrupt them and allow the run function to cleanly exit.

You just have to pay attention to the fact that alarm uses signal under the hood so you must not block SIG_ALRM, but you can use it to install a signal handler that stores somewhere that is has been called.

IMHO, it will be simpler, cleaner, and with a better separation of concern than directly terminating the program with std::terminate.

Above only deals with the case where run never ends. If you want to limit the time it runs, you should identify interruptible places in your code where you test if allowed run time is exhausted, and consistently put timeouts on all possibly blocking communication IO.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • You're not answering my question. `run()` does end. But it could take more time than expected. I want to force `run()` to finish in the case it was too long. It could be blocked in a communication read or not. – didil Nov 10 '15 at 10:03
1

I guess you are on Linux or some other POSIX system. Event loops and polling are not standardized in C++11 and need operating system specific things.

Your event loop should never be blocked for a long time. It should have some finite -and not too big- timeout. On POSIX, use poll(2) in your event loop with a reasonable timeout (e.g. a second). Alternatively, use a pipe (internal to the process) to trigger the event loop (so some other thread -or even a signal handler- would write(2) on that pipe, and the event loop would poll it and read it, and might stop, hence returning from run)

See also this and that for related hints.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

The best solution is to wrap run() in a thread.

std::thread([&]()
{
   run();
   finish.notify_all();
}).detach();

std::unique_lock<std::mutex> lock(waitFinish);
finish.wait_for(lock, time);
didil
  • 693
  • 8
  • 22