1

Let's say I have a foo() function. I want it to run in, for example, 5 seconds, after that, it has to be cancelled and continues to do the rest of the program.

Code snippets:

int main() {
    // Blah blah
    foo(); // Running in 5 sec only
    // After 5 sec, came here and finished
}

References: After a while searching on StackOverflow, I found this is what I need but written in python: Timeout on a function call.

signal.h and unistd.h can be related.

John Williams
  • 33
  • 1
  • 7
  • Is the function responsible for deciding it has taken too long, or does another external thread decide that and need to tell the function to stop? – 1201ProgramAlarm Feb 06 '20 at 15:42
  • Do you need to preserve any side-effects produced by `foo()` in the interim, or can you throw away any partial result and declare it took too long? – jxh Feb 06 '20 at 15:49
  • I think I want another function to handle when it's time to terminate `foo()`. @1201ProgramAlarm – John Williams Feb 06 '20 at 15:52
  • Use the example you found and translate it in C++ (POSIX API : sigaction, alarm). – Jean-Baptiste Yunès Feb 06 '20 at 15:54
  • Now, what I need is a `void foo()` (no result returned). Perhaps, I want to preserve some results from `bar()` later. So, in general, both of them is the best. @jxh – John Williams Feb 06 '20 at 15:57
  • @JohnWilliams: Then it is best if you write `foo()` and `bar()` to both know when they should quit. – jxh Feb 06 '20 at 15:59
  • Can you make changes to `foo`? – 1201ProgramAlarm Feb 06 '20 at 16:02
  • what is `foo` ? what should happen to `foo` when the timeout expired? I mean obviously it has side effects, so code after that would depend on the sucessful execution of `foo`, no?. Ultimately, this smells like a [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Why do you think you need to do that? What do you actually want to achieve with this? – 463035818_is_not_an_ai Feb 06 '20 at 16:02
  • 1
    @idclev463035818: Sometimes unit test code has this property. Sometimes certain numerical calculations must be cut off after some amount of time. But for the former, you treat the partial run as failure, so you don't care about the partial result. For the latter, the routine itself will monitor whether or not it should end with the partial calculation (the result didn't converge, for example). – jxh Feb 06 '20 at 16:06
  • Python handles interrupting a thread or function by injecting a `raise` wherever the thread might be. This is pretty safe and a clean way of doing things. However, this isn't possible in c++. Your function has to be written to be interruptible, it isn't behavior that can be injected into existing functions without changing them. – François Andrieux Feb 06 '20 at 16:06
  • @Jean-BaptisteYunès This what I attempted to translate from python to C++. – John Williams Feb 06 '20 at 16:09
  • 1
    @jxh for unit test I would not care about unclean termination of `foo`. For numerical calculations on the other hand I would never want to simply terminate it, but include a means for early termination **inside** `foo` such that I can easily gather information on the state of calculation at the point it timed out. Whatever it is, imho a good solution depends on what `foo` actually is and why it needs a timeout – 463035818_is_not_an_ai Feb 06 '20 at 16:10
  • @jxh sometimes I am writing in a rush. only after writing the comment i realized that you basically said the same :). perhaps I need a bit less coffee – 463035818_is_not_an_ai Feb 06 '20 at 16:12
  • @JohnWilliams It seems that it's unclear whether or not you are allowed to modify `foo` to accommodate your goal. Can you clarify? – François Andrieux Feb 06 '20 at 16:15
  • I want to preserve what is inside `foo()` and have a function, let's say, `foo_wrapper()` call `foo()` which determines when to terminate `foo()` – John Williams Feb 06 '20 at 16:23
  • Given the generic requirements, I would implement a `libgdb` loop to debug `foo()` executing within a `fork()`-ed child process. I admit, it is overkill, but I can't think of a general way to deal with partial execution results. After using the appropriate `libgdb` commands to read the relevant function variables, kill the child. – jxh Feb 06 '20 at 16:46

3 Answers3

2

This is possible with threads. Since C++20, it will be fairly simple:

{
    std::jthread t([](std::stop_token stoken) {
        while(!stoken.stop_requested()) {
            // do things that are not infinite, or are interruptible
        }
    });

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(5s);
}

Note that many interactions with the operating system cause the process to be "blocked". An example of such is the POSIX function listen, which waits for incoming connections. If the thread is blocked, then it will not be able to proceed to the next iteration.

Unfortunately, the C++ standard doesn't specify whether such platform specific calls should be interrupted by request to stop or not. You need to use platform specific methods to make sure that happens. Typically, signals can be configured to interrupt blocking system calls. In case of listen, an option is to connect to the waiting socket.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

There is no way to do that uniformly in C++. There are ways to do this with some degree of success when you use OS specific APIs, however it all becomes extremely cumbersome.

The basic idea which you can use in *nix is a combination of alarm() system call and setjmp/longjmp C function.

A (pseudo) code:

std::jmp_buf jump_buffer;

void alarm_handle(int ) {
    longjmp(jump_buffer);
}

int main() {
   signal(SIGALRM, alarm_handle); 
    alarm(5);
    if (setjmp(jump_buffer)) {
        foo(); // Running in 5 sec only
     } else {
        // After 5 sec, came here and finished
        // if we are here, foo timed out
     } 
}

This all is extremely fragile and shaky (i.e. long jumps do not place nicely with C++ objects lifetime), but if you know what you are doing this might work.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 3
    This bypasses any destructors inside `foo()`, and it may cause undefined behavior since the signal may have been delivered while `foo()` was inside some other library function, and the `else` case tries to enter the same function. – jxh Feb 06 '20 at 15:58
  • You'll also need to "silence the alarm" if `foo` completes before it goes off, which will also open a window where `foo` completes but the alarm stiil goes off before it can be silenced. – 1201ProgramAlarm Feb 06 '20 at 16:01
  • @jxh I mentioned bypassing destructors in my answer as well, and there is nothing specifically here to trigger undefined behavior. – SergeyA Feb 06 '20 at 16:07
  • @1201ProgramAlarm silencing an alarm should happen within foo instead, after it completes. – SergeyA Feb 06 '20 at 16:08
  • @SergeyA It's assumed you can't change `foo`. Otherwise you could just implement any number of better solutions if you can change `foo`. – François Andrieux Feb 06 '20 at 16:08
  • @FrançoisAndrieux I think it is rather moot point. You can have foo_wrap which calls foo and silences alarm after foo returns. – SergeyA Feb 06 '20 at 16:09
  • As a simple example, `foo()` may have been in the middle of allocating more memory when it was interrupted. The heap data structure is now in an inconsistent state. If the `else` part attempts to allocate memory, the program may crash. – jxh Feb 06 '20 at 16:10
  • It's true that this solution seems to be the only one that exists without changing `foo` but it's very bad. The reality is that functions aren't interuptible in c++ unless they are designed to be. – François Andrieux Feb 06 '20 at 16:12
  • @jxh it might. It also might have other things in it, which is not compatible with signals/long jumps, like I said. – SergeyA Feb 06 '20 at 16:29
  • If you `longjmp()` out of a signal handler, then where it lands, the code afterward is generally limited to the same restrictions as the signal handler code itself. Really, the only suitable solution is to `_exit()`, and that could be done inside the signal handler rather than jumping out. – jxh Feb 06 '20 at 16:35
  • @jxh this is not true. You can long jump out of signal handler: https://www.gnu.org/software/libc/manual/html_node/Longjmp-in-Handler.html – SergeyA Feb 06 '20 at 18:42
  • @SergeyA If something I said led you to believe that, my apologies. My point is if `foo()` is a black box, then you are limited to actions that would be safe for the signal handler after performing the jump. – jxh Feb 06 '20 at 19:04
0

Perfectly standard C++11

#include <iostream>
#include <thread>         // std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds

using namespace std;

// stop flag
bool stopfoo;

// function to run until stopped
void foo()
{
    while( ! stopfoo )
    {
        // replace with something useful
        std::this_thread::sleep_for (std::chrono::seconds(1));
        std::cout << "still working!\n";
    }
    std::cout "stopped\n";
}

// function to call a top after 5 seconds
void timer()
{
    std::this_thread::sleep_for (std::chrono::seconds( 5 ));
    stopfoo = true;
}

int main()
{
    // initialize stop flag
    stopfoo = false;

    // start timer in its own thread
    std::thread t (timer);

    // start worker in main thread
    foo();

    return 0;
}

Here is the same thing with a thread safe stop flag ( not really neccessary, but good practice for more complex cases )

#include <iostream>
#include <thread>         // std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds
#include <mutex>

using namespace std;

class cFlagThreadSafe
{
public:
    void set()
    {
        lock_guard<mutex> l(myMtx);
        myFlag = true;
    }
    void unset()
    {
        lock_guard<mutex> l(myMtx);
        myFlag = false;
    }
    bool get()
    {
         lock_guard<mutex> l(myMtx);
         return myFlag;
    }
private:
    bool myFlag;
    mutex myMtx;
};

// stop flag
cFlagThreadSafe stopfoo;

// function to run until stopped
void foo()
{
    while( ! stopfoo.get() )
    {
        // replace with something useful
        this_thread::sleep_for (std::chrono::seconds(1));
        cout << "still working!\n";
    }
    cout << "stopped\n";
}

// function to call a top after 5 seconds
void timer()
{
    this_thread::sleep_for (chrono::seconds( 5 ));
    stopfoo.set();
}

int main()
{
    // initialize stop flag
    stopfoo.unset();

    // start timer in its own thread
    thread t (timer);

    // start worker in main thread
    foo();

    t.join();

    return 0;
}

And if it is OK to do everything in the main thread, things can be greatly simplified.

#include <iostream>
#include <thread>         // std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds

using namespace std;

void foo()
{
    auto t1 = chrono::steady_clock ::now();

    while( chrono::duration_cast<chrono::seconds>(
                chrono::steady_clock ::now() - t1 ).count() < 5 )
    {
        // replace with something useful
        this_thread::sleep_for (std::chrono::seconds(1));
        cout << "still working!\n";
    }
    cout << "stopped\n";
}

int main()
{
    // start worker in main thread
    foo();

    return 0;
}
ravenspoint
  • 19,093
  • 6
  • 57
  • 103
  • 2
    `stopfoo` should be atomic. – 1201ProgramAlarm Feb 06 '20 at 16:18
  • Actually it should be protected by a mutex. However, in practice is does not matter and this demo software does not need it. If OP does not know how to use mutexes, they should ask another question. – ravenspoint Feb 06 '20 at 16:24
  • IMHO std::async() and all the rest of that is unnecessary complication. I have never found it needed. – ravenspoint Feb 06 '20 at 16:41
  • @1201ProgramAlarm why stopfoo should be atomic? – SPD Feb 06 '20 at 17:00
  • @SPD To avoid a data race on `stopfoo`. One consequence of this is that it is possible (albeit unlikely) that the code in the loop in `foo` is written so that the compiler can determine that nothing in the loop will change `stopfoo`, so it just might turn the loop in `foo` into an infinite one. A mutex can also be used. However, most systems will run the code correctly without an atomic or mutex. – 1201ProgramAlarm Feb 06 '20 at 17:15
  • @1201ProgramAlarm are you suggesting compiler could optimize the while-loop in this example to make it an infinite loop? Can you please provide any reference for this argument? Unless stopfoo is declared as a const variable(which is not the case here), I can't see how a compiler can do this. – SPD Feb 06 '20 at 17:57
  • 1
    A compiler can certainly decide a loop does not modify a variable, and then adjusts the emitted code to treat the variable like a constant. Normally, you use `volatile` to avoid that. You use an atomic to prevent the code from doing a read on a partially written value, since that could cause undefined behavior (like if the partial read decoded into a trap value). – jxh Feb 06 '20 at 18:13
  • 2
    @ jxh I understand a compiler can optimize a loop if it determines the condition doesn't change and the use of volatile keyword to prevent such optimization. What I don't understand is how a compile can optimize the while loop in *this* example (to my knowledge, such optimization typically happens when the condition is const, which is not the case here!). Please can you elaborate? Also regarding "partial read", in what platform a partial read can happen on a boolean that we're talking about here? – SPD Feb 06 '20 at 18:22
  • @SPD Lifting the calculation of values that won't change during a loop to outside of the loop is a pretty common optimization (loop invariant). In the case of a global variable, if nothing in the loop changes that variable, and there are no non-inlined function calls (that could change the global variable), the `stopfoo != 0` can become a loop invariant and be removed from the loop. Rather unlikely in this particular case, but it is a theoretical possibility that should be addressed for others that find this question/answer. – 1201ProgramAlarm Feb 06 '20 at 22:36