views:

2499

answers:

3

I am running function Foo from somebody else's library in a single-threaded application currently. Most of the time, I make a call to Foo and it's really quick, some times, I make a call to Foo and it takes forever. I am not a patient man, if Foo is going to take forever, I want to stop execution of Foo and not call it with those arguments.

What is the best way to call Foo in a controlled manner (my current environment is POSIX/C++) such that I can stop execution after a certain number of seconds. I feel like the right thing to do here is to create a second thread to call Foo, while in my main thread I create a timer function that will eventually signal the second thread if it runs out of time.

Is there another, more apt model (and solution)? If not, would Boost's Signals2 library and Threads do the trick?

+1  A: 

You can also set an alarm right before calling that function, and catch SIGALRM.

FreeMemory
+3  A: 

You can call Foo on a second thread with a timeout. For example:

#include <boost/date_time.hpp> 
#include <boost/thread/thread.hpp>

boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(500);
boost::thread thrd(&Foo);

if (thrd.timed_join(timeout))
{
  //finished
}
else
{
  //Not finished;
}
Dani van der Meer
Just to clarify, `timed_join` *won't* stop the `Foo()` thread's execution if the timeout is reached, as I thought Aron had asked for. Instead, the caller will merely know that the `Foo()` thread was still running when the timeout was reached.
pilcrow
A: 

You can use the following class:

class timer
{
    typedef boost::signals2::signal<void ()> timeout_slot;
public:
    typedef timeout_slot::slot_type timeout_slot_t;

public:
    timer() : _interval(0), _is_active(false) {};
    timer(int interval) : _interval(interval), _is_active(false) {};
    virtual ~timer() { stop(); };

    inline boost::signals2::connection connect(const timeout_slot_t& subscriber) { return _signalTimeout.connect(subscriber); };

    void start()
    {
        boost::lock_guard<boost::mutex> lock(_guard);

        if (is_active())
            return; // Already executed.
        if (_interval <= 0)
            return;

        _timer_thread.interrupt();
        _timer_thread.join();

        timer_worker job;
        _timer_thread = boost::thread(job, this);

        _is_active = true;
    };

    void stop()
    {
        boost::lock_guard<boost::mutex> lock(_guard);

        if (!is_active())
            return; // Already executed.

        _timer_thread.interrupt();
        _timer_thread.join();

        _is_active = false;
    };

    inline bool is_active() const { return _is_active; };

    inline int get_interval() const { return _interval; };

    void set_interval(const int msec)
    {
        if (msec <= 0 || _interval == msec)
            return;

        boost::lock_guard<boost::mutex> lock(_guard);
        // Keep timer activity status.
        bool was_active = is_active();

        if (was_active)
            stop();
        // Initialize timer with new interval.
        _interval = msec;

        if (was_active)
            start();
    };

protected:
    friend struct timer_worker;
    // The timer worker thread.
    struct timer_worker
    {
        void operator()(timer* t)
        {
            boost::posix_time::milliseconds duration(t->get_interval());

            try
            {
                while (1)
                {
                    boost::this_thread::sleep<boost::posix_time::milliseconds>(duration);
                    {
                        boost::this_thread::disable_interruption di;
                        {
                            t->_signalTimeout();
                        }
                    }
                }
            }
            catch (boost::thread_interrupted const& )
            {
                // Handle the thread interruption exception.
                // This exception raises on boots::this_thread::interrupt.
            }
        };
    };

protected:
    int             _interval;
    bool            _is_active;

    boost::mutex    _guard;
    boost::thread   _timer_thread;

    // Signal slots
    timeout_slot    _signalTimeout;
};

An example of usage:

void _test_timer_handler()
{
    std::cout << "_test_timer_handler\n";
}

BOOST_AUTO_TEST_CASE( test_timer )
{
    emtorrus::timer timer;

    BOOST_CHECK(!timer.is_active());
    BOOST_CHECK(timer.get_interval() == 0);

    timer.set_interval(1000);
    timer.connect(_test_timer_handler);

    timer.start();

    BOOST_CHECK(timer.is_active());

    std::cout << "timer test started\n";

    boost::this_thread::sleep<boost::posix_time::milliseconds>(boost::posix_time::milliseconds(5500));

    timer.stop();

    BOOST_CHECK(!timer.is_active());
    BOOST_CHECK(_test_timer_count == 5);
}