Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

timeout in std::async

Is there a way to implement timeout in the std::async method, so I want this call to timeout and complete if the thread hasnt completed for the specified amount of time. How can I implement this functionality.

like image 216
Adobri Avatar asked Jan 07 '13 20:01

Adobri


2 Answers

There is no (standard) way to reach into a thread and kill it, and this is generally a bad idea anyway. The much cleaner option is to pass a starting time and max duration to the function and then (possibly multiple times as the calculation progresses) check if the current time minus the starting time is too long.

I would make something like this:

#include <chrono>

template <typename Clock = std::chrono::steady_clock>
class timeout
{
public:
    typedef Clock clock_type;
    typedef typename clock_type::time_point time_point;
    typedef typename clock_type::duration duration;

    explicit timeout(duration maxDuration) :
    mStartTime(clock_type::now()),
    mMaxDuration(maxDuration)
    {}

    time_point start_time() const
    {
        return mStartTime;
    }

    duration max_duration() const
    {
        return mMaxDuration;
    }

    bool is_expired() const
    {
        const auto endTime = clock_type::now();

        return (endTime - start_time()) > max_duration();
    }

    static timeout infinity()
    {
        return timeout(duration::max());
    }

private:
    time_point mStartTime;
    duration mMaxDuration;
};

This simple utility tracks the starting time and a max duration (and provides a way of specifying infinity), and allows the user to query simple facts, most importantly whether or not a timeout has occurred.

Test below; you can add fake delay by defining/undefining FAKE_DELAY:

#include <iostream>
#include <future>

#define FAKE_DELAY

void fake_delay()
{
    #ifdef FAKE_DELAY
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    #endif
}

void short_running_function(timeout<> timelimit)
{
    fake_delay();

    if (timelimit.is_expired())
        std::cout << "short running thread ran out of time" << std::endl;
    else
        std::cout << "short running function finished" << std::endl;
}

void long_running_function(timeout<> timelimit)
{
    for (unsigned i = 0; i < 10; ++i) {
        if (timelimit.is_expired())
        {
            std::cout << "long running thread ran out of time" << std::endl;
            return;
        }

        std::cout << "long running thread doing work" << std::endl;
        fake_delay();
    }

    std::cout << "long running function finished" << std::endl;
}

int main()
{
    std::async(short_running_function,
               timeout<>(std::chrono::milliseconds(500))).wait();

    std::async(short_running_function,
               timeout<>(std::chrono::milliseconds(5000))).wait();

    std::async(long_running_function,
               timeout<>(std::chrono::milliseconds(500))).wait();

    std::async(long_running_function,
               timeout<>(std::chrono::milliseconds(5000))).wait();

    std::async(long_running_function,
               timeout<>::infinity()).wait();
}

One possible output with FAKE_DELAY off:

short running function finished
short running function finished
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running function finished
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running function finished
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running function finished

One possible output with FAKE_DELAY on:

short running thread ran out of time
short running function finished
long running thread doing work
long running thread ran out of time
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread ran out of time
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running thread doing work
long running function finished

like image 181
GManNickG Avatar answered Oct 30 '22 00:10

GManNickG


The standard does not provide any way to non-cooperatively kill a thread. The lack of automatic timeout functionality is just another example of this.

Instead you must implement the code you pass to std::async to handle doing this cooperatively. One pattern for cooperatively killing a thread is to pass to the function an object which provides a method to check if the async function should continue, and if not it throws an exception.

struct Time_out {
    std::chrono::steady_clock start = std::chrono::steady_clock::now();
    std::chrono::milliseconds timeout;
    Time_out(std::chrono::milliseconds t) : timeout(t) {}

    void check() {
        if (start + timeout < std::chrono::steady_clock::now())
            throw timeout_exception();
    }
};

std::future<void> f = std::async([](Time_out t) {
    while(more work) {
        // do work
        t.check();
    }
}, Time_out(std::chrono::seconds(2));

f.get();
like image 33
bames53 Avatar answered Oct 29 '22 23:10

bames53