Program Listing for File timed_base.hpp¶
↰ Return to documentation for file (stream-client/detail/timed_base.hpp
)
#pragma once
#include <boost/asio/basic_waitable_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <chrono>
#include <iostream>
namespace stream_client {
namespace detail {
template <typename Clock>
class timed_base
{
public:
using clock_type = Clock;
using time_duration_type = typename clock_type::duration;
using time_point_type = typename clock_type::time_point;
using timer_type = typename boost::asio::basic_waitable_timer<clock_type>;
static constexpr time_duration_type kInfiniteDuration = time_duration_type::max();
static constexpr time_duration_type kZeroDuration = time_duration_type(0);
static constexpr time_duration_type kDurationResolution = time_duration_type(1000); // 1us
static constexpr time_duration_type kMinTimeout = 2 * kDurationResolution;
class expiration final
{
public:
expiration(timer_type* timer = nullptr)
: timer_(timer)
{
}
expiration(timer_type* timer, const time_duration_type& duration)
: expiration(timer)
{
if (duration < kDurationResolution) {
throw boost::system::system_error{boost::asio::error::timed_out};
}
if (duration == kInfiniteDuration) {
timer_ = nullptr;
return;
}
if (timer_) {
timer_->expires_from_now(duration);
}
}
expiration(timer_type* timer, const time_point_type& deadline)
: expiration(timer)
{
if (timer_) {
timer_->expires_at(deadline);
}
}
// copy ctor
expiration(const expiration& other) = default;
// move ctor
expiration(expiration&& other) noexcept
{
*this = std::move(other);
}
// copy assignment
inline expiration& operator=(const expiration& other) = default;
// move assignment
inline expiration& operator=(expiration&& other) noexcept
{
timer_ = std::exchange(other.timer_, nullptr);
return *this;
}
~expiration()
{
if (timer_) {
timer_->expires_from_now(kInfiniteDuration);
}
}
private:
timer_type* timer_;
};
timed_base()
: deadline_fired_(false)
, io_service_(std::make_unique<boost::asio::io_service>())
, timer_(std::make_unique<timer_type>(*io_service_))
{
// No deadline is required until the first operation is started. We
// set the deadline to positive infinity so the actor takes no action
// until a specific deadline is set.
timer_->expires_from_now(kInfiniteDuration);
post_deadline();
}
timed_base(const timed_base<Clock>& other) = delete;
timed_base<Clock>& operator=(const timed_base<Clock>& other) = delete;
timed_base(timed_base<Clock>&& other) = default;
timed_base& operator=(timed_base<Clock>&& other) = default;
virtual ~timed_base()
{
if (io_service_) {
io_service_->stop();
}
}
inline boost::asio::io_service& get_io_service()
{
return *io_service_;
}
inline const boost::asio::io_service& get_io_service() const
{
return *io_service_;
}
template <typename Time>
expiration scope_expire(const Time& timeout_or_deadline)
{
deadline_fired_ = false;
return expiration(timer_.get(), timeout_or_deadline);
}
protected:
virtual void deadline_actor() = 0;
bool deadline_fired_;
private:
inline void post_deadline()
{
timer_->async_wait([this](const boost::system::error_code& ec) { this->check_deadline(ec); });
}
void check_deadline(const boost::system::error_code& /*ec*/)
{
if (timer_->expires_at() <= clock_type::now()) {
deadline_actor();
deadline_fired_ = true;
timer_->expires_from_now(kInfiniteDuration);
}
// if deadline fired or reset to new one, check_deadline() gets reposted
post_deadline();
}
std::unique_ptr<boost::asio::io_service> io_service_;
std::unique_ptr<timer_type> timer_;
};
template <typename Clock>
constexpr typename timed_base<Clock>::time_duration_type timed_base<Clock>::kInfiniteDuration;
template <typename Clock>
constexpr typename timed_base<Clock>::time_duration_type timed_base<Clock>::kZeroDuration;
template <typename Clock>
constexpr typename timed_base<Clock>::time_duration_type timed_base<Clock>::kDurationResolution;
template <typename Clock>
constexpr typename timed_base<Clock>::time_duration_type timed_base<Clock>::kMinTimeout;
using system_timed_base = timed_base<std::chrono::system_clock>;
using steady_timed_base = timed_base<std::chrono::steady_clock>;
using high_resolution_timed_base = timed_base<std::chrono::high_resolution_clock>;
} // namespace detail
} // namespace stream_client