Timers

159

Let's take a break from multithreading execution models and learn something really easy. A kind of tool you need in almost any application dealing with I/O is timer. So let's learn how to use them in Boost.Asio.

There are four timers offered:

boost::asio::deadline_timer
boost::asio::high_resolution_timer
boost::asio::steady_timer
boost::asio::system_timer

However all of them are offering the same functionality. The difference between them is that deadline_timer is based on boost::posix_time when the others are based on std::chrono timers. So there's no really need to discuss all of them in details since they offer the same interface and behavior type. Let's look at the example of high_resolution_timer usage.

#include <boost/asio.hpp>
#include <iostream>

namespace io = boost::asio;
using error_code = boost::system::error_code;

io::io_context io_context;

// A timer should be constructed from io_context reference
io::high_resolution_timer timer(io_context);

auto now()
{
    return std::chrono::high_resolution_clock::now();
}

auto begin = now();

void async_wait()
{
    // Set the expiration duration
    timer.expires_after(std::chrono::seconds(1));

    // Wait for the expiration asynchronously
    timer.async_wait([&] (error_code error)
    {
        auto elapsed = std::chrono::duration_cast<std::chrono::seconds>(now() - begin).count();
        std::cout << elapsed << "\n";
        async_wait();
    });
}

int main()
{
    async_wait();
    io_context.run();
    return 0;
}

In the example above we wait for a period of a given time (one second) asynchronously and execute a completion handler after that. As well as the other I/O entities, a timer is attached to io_context object and it handlers are executed within its io_context::run function loop.

You can specify an absolute time point of the timer's expiration instead of a time duration. Use timer's expires_at member function for that with a corresponding time_point value:

timer.expires_at(std::chrono::high_resolution_clock::now() + std::chrono::seconds(1));

To get a timer's expiration time_point use its expiry member function:

std::chrono::high_resolution_clock::time_point time_point = timer.expiry();

Waiting for the timer expiration can be canceled with timer's cancel member function. In that case boost::asio::error::operation_aborted error is passed into the timer's completion handler:

timer.async_wait([&] (error_code error)
{
    if(error == boost::asio::error::operation_aborted)
    {
        std::cout << "The timer is cancelled\n";
    }
});

// ---

timer.cancel();

A cancellation of a currently waiting timer is thread safe. However you should take an error code into consideration to cancel your asynchronous logic properly.

Share this page:

Learning plan

How execute a regular code within io_context::run polling loop
We've dealt with a single-threaded environment so far; now it's time to go multithreading
A special execution model with a custom load balancer
14. Timers
Working with asynchronous timers within io_context polling loop
What's the difference between a client and a server, and what do they have in common
Resolving hostnames into IP addresses before connect
Writing a very simple client application in C++ with Boost.Asio