boost::asio timer

Keywords: network

asio

asio was originally an independent network library, which was selected by boost and transformed into a part of boost library. The design principle is light inheritance and recombination.
It conforms to the consistent style of c++.

Base class: boost::asio::io_service
 Timer class: boost::asio::basic_waitable_timer
 Clock class: std::chrono::steady_clock

This paper is a part of the kademlia algorithm, which is well understood by annotations. No detailed explanation is prepared.

//timer.hpp
#ifndef KADEMLIA_TIMER_HPP
#define KADEMLIA_TIMER_HPP

#ifdef _MSC_VER
#pragma once
#endif

#include <map>
#include <chrono>
#include <functional>
#include <boost/asio/io_service.hpp>
#include <boost/asio/basic_waitable_timer.hpp>

namespace kademlia
{
	namespace detail {

		class timer final {

		public:

			using clock =	std::chrono::steady_clock;

			using duration = clock::duration;


		public:

			explicit timer( boost::asio::io_service & io_service);

			template < typename Callback > void expires_from_now(duration const & timeout,
				 Callback const & on_timer_expired);


		private:

			using time_point = clock::time_point;

			using callback = std::function < void ( void ) >;

			using timeouts = std::multimap < time_point, callback >;

			using deadline_timer = boost::asio::basic_waitable_timer < clock >;


		private:

			void schedule_next_tick(time_point const & expiration_time);


		private:

			deadline_timer	timer_;			// asio timer

			timeouts		timeouts_;		// Timing task map, sorted from small to large
		};


		// Register a business with the timer. The execution point is current time + timeout, and the execution function is on_timer_expired.
		template < typename Callback >
		void timer::expires_from_now(duration const & timeout,
			 Callback const & on_timer_expired)
		{
			auto			expiration_time = clock::now() + timeout;

			// If the current expiration time will be the sooner to expires
			// then cancel any pending wait and schedule this one instead.
			// If the timer map is empty or the timer is the latest, then use the timer to update the asio timer and add the timer to the timer map.
			if (timeouts_.empty() || expiration_time < timeouts_.begin()->first)
				schedule_next_tick(expiration_time);

			timeouts_.emplace(expiration_time, on_timer_expired);
		}

	}
}


#endif


//timer.cpp
#include "kademlia/timer.hpp"

#include "kademlia/error_impl.hpp"
#include "kademlia/log.hpp"

namespace kademlia
{
	namespace detail {

		// To construct an ASIO timer, io_service is the base class of asio, and any object needs to be embedded in it. The general usage is specified when an object is constructed.
		timer::timer(boost::asio::io_service & io_service): timer_ {io_service}, timeouts_ {}
		{
		}

		void timer::schedule_next_tick(time_point const & expiration_time) {
			// This will cancel any pending task.
			// Setting timeout point
			timer_.expires_at(expiration_time);

			LOG_DEBUG(timer, this) << "schedule callback at " << expiration_time.time_since_epoch().count() << "." << std::endl;

			auto			on_fire =[this] (boost::system::error_code const & failure) {
				// The current timeout has been canceled
				// hence stop right there.
				if (failure == boost::asio::error::operation_aborted)
					return;

				if (failure)
					throw std::system_error {
						make_error_code(TIMER_MALFUNCTION)
					};

				// The callbacks to execute are the first
				// n callbacks with the same keys.
				auto			begin = timeouts_.begin();
				auto			end = timeouts_.upper_bound(begin->first);

				// Call the user callbacks. Perform timing services
				for (auto i = begin; i != end; ++i)
					i->second();

				LOG_DEBUG(timer, this) << "remove " << std::distance(begin, end) << " callback(s) scheduled at " << begin->first.time_since_epoch().count() << "." << std::endl;

				// And remove the timeout.
				timeouts_.erase(begin, end);

				// If there is a remaining timeout, schedule it.
				if (!timeouts_.empty()) {
					LOG_DEBUG(timer, this) << "schedule remaining timers" << std::endl;
					schedule_next_tick(timeouts_.begin()->first);
				}
			};

			// Executing timeout tasks when asynchronous, timeout point arrives
			timer_.async_wait(on_fire);
		}

	}
}

All objects of asio provide two versions of sync and async. The async used in this article is very convenient for timer update or scheduling because it is asynchronous.
As for the use of asio, csdn has a lot on it, but at the very most, it's the same level, so it doesn't repeat wheels.

Posted by iamatube on Mon, 30 Sep 2019 13:59:23 -0700