Timers
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.