Pages

Simple deadline timer

We can use the I/O service object that boost::asio makes available to us to set a timeout both in a synchronous or asynchronous way. Here is a simple example of an asynchronous one.

This post is ancient (February 2011). I would suggest you to follow the link to its updated version (March 2018) instead of reading it.

The program we are about to create sets a three seconds deadline timer on an I/O service object. Then, from the main thread, we create another thread that is going to do some obscure job. When the deadline occurs, we stop the job, and that terminates the program.

This tiny application makes use of a couple of functors. First one, MyJob, encapsulates the job executed by the application in a worker thread till the deadline expires:
class MyJob : public boost::noncopyable // 1
{
private:
    boost::mutex mx_;
    bool outOfTime_; // 2
public:
    MyJob() : outOfTime_(false) {}

    void log(const char* message) // 3
    {
        boost::lock_guard<boost::mutex> lock(mx_);
        std::cout << message << std::endl;
    }

    void timeout() // 4
    {
        outOfTime_ = true;
        log("Timeout!");
    }

    void operator()() // 5
    {
        for(int i = 0; !outOfTime_; ++i)
        {
            std::ostringstream os;
            os << '[' << i << ']';

            log(os.str().c_str());
            boost::this_thread::sleep(boost::posix_time::seconds(1));
        }
    }
};
1. This class can't be copied. No copy ctor or assignment operator is defined for it, not even the default one. Making it deriving from boost::noncopiable is a compact way of saying that.
2. outOfTime_ is a flag, initialized by the ctor to false, that is set to true to stop the infinite loop of this functor.
3. Utility method to do some logging.
4. When the timer expires, it calls this method, to signal that we want to terminate the infinite loop.
5. A loop to output the current delay till we go out of time.

The other functor is required by the timer:
class MyWaitHandler
{
private:
    MyJob& job_; // 1

public:
    MyWaitHandler(MyJob& job) : job_(job) {}

    void operator()(const boost::system::error_code&)
    {
        job_.timeout(); // 2
    }
};
1. Reference to the running job, so we can call it and ask it to stop.
2. When the function is called, we simply signal the timeout occurrence to the job. We don't even check the error status, whatever happens we just terminate.

And here is the main routine for our program:
boost::asio::io_service io; // 1
boost::asio::deadline_timer timer(io, boost::posix_time::seconds(3)); // 2

MyJob job; // 3
timer.async_wait(MyWaitHandler(job)); // 4

boost::thread thread(std::ref(job)); // 5
io.run();
job.log("I/O service completed");

thread.join();
job.log("Local thread completed");
1. First thing is creating a I/O service object.
2. We set a deadline timer on our I/O service object.
3. We create the job, that would run in another thread.
4. We say to the deadline timer to wait asynchronously until the expiration of the timer, then call the passed functor - created passing the job by reference.
5. Before running the I/O service, we create a thread on our job - passed by reference (and here we need to use std::ref to force this requirement).

Post based on an example from the official Boost ASIO tutorial. The full C++ source code for this variation is available on github.

2 comments:

  1. hello,

    why wouldn't the timer invloke directly the timeout method? (I don't think MyWaitHandler is necessary in this case)

    here is what I mean:

    MyJob job; // 3

    timer.async_wait( boost::bind(&MyJob::timeout, boost::ref(job)) ); // 4

    boost::thread thread(std::ref(job)); // 5


    Thanks,

    ReplyDelete
    Replies
    1. Actually, I'd say you are right. I don't remember anymore what was the point I wanted to make when I wrote the code in that way. Probably showing less concisely the concept, to make it more understandable.

      Thank you for sharing your solution!

      Delete