/* * Copyright (c) 2017-2023 zhllxt * * author : zhllxt * email : 37792738@qq.com * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef __ASIO2_SILENCE_TIMER_COMPONENT_HPP__ #define __ASIO2_SILENCE_TIMER_COMPONENT_HPP__ #if defined(_MSC_VER) && (_MSC_VER >= 1200) #pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include namespace asio2::detail { template class silence_timer_cp { public: /** * @brief constructor */ silence_timer_cp() = default; /** * @brief destructor */ ~silence_timer_cp() = default; public: /** * @brief get silence timeout value */ inline std::chrono::steady_clock::duration get_silence_timeout() const noexcept { return this->silence_timeout_; } /** * @brief set silence timeout value */ template inline derived_t & set_silence_timeout(std::chrono::duration duration) noexcept { if (duration > std::chrono::duration_cast< std::chrono::duration>((std::chrono::steady_clock::duration::max)())) this->silence_timeout_ = (std::chrono::steady_clock::duration::max)(); else this->silence_timeout_ = duration; return static_cast(*this); } protected: template inline void _post_silence_timer( std::chrono::duration duration, std::shared_ptr this_ptr) { derived_t& derive = static_cast(*this); asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(), [this, this_ptr = std::move(this_ptr), duration]() mutable { derived_t& derive = static_cast(*this); #if defined(_DEBUG) || defined(DEBUG) ASIO2_ASSERT(this->is_stop_silence_timer_called_ == false); #endif // reset the "canceled" flag to false, see reconnect_timer_cp.hpp -> _make_reconnect_timer this->silence_timer_canceled_.clear(); // start the timer of check silence timeout if (duration > std::chrono::duration::zero()) { if (this->silence_timer_ == nullptr) { this->silence_timer_ = std::make_unique(derive.io_->context()); } this->silence_timer_->expires_after(duration); this->silence_timer_->async_wait( [&derive, this_ptr = std::move(this_ptr)](const error_code & ec) mutable { derive._handle_silence_timer(ec, std::move(this_ptr)); }); } })); } inline void _handle_silence_timer(const error_code & ec, std::shared_ptr this_ptr) { derived_t& derive = static_cast(*this); ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted); // ec maybe zero when timer_canceled_ is true. if (ec == asio::error::operation_aborted || this->silence_timer_canceled_.test_and_set()) { this->silence_timer_.reset(); return; } this->silence_timer_canceled_.clear(); // silence duration seconds not exceed the silence timeout,post a timer // event agagin to avoid this session shared_ptr object disappear. std::chrono::system_clock::duration silence = derive.get_silence_duration(); if (silence < this->silence_timeout_) { derive._post_silence_timer(this->silence_timeout_ - silence, std::move(this_ptr)); } else { // silence timeout has elasped,but has't data trans,don't post // a timer event again,so this session, shared_ptr will disappear // and the object will be destroyed automatically after this handler returns. set_last_error(asio::error::timed_out); derive._do_disconnect(asio::error::timed_out, std::move(this_ptr)); this->silence_timer_.reset(); } } inline void _stop_silence_timer() { derived_t& derive = static_cast(*this); derive.dispatch([this]() mutable { #if defined(_DEBUG) || defined(DEBUG) this->is_stop_silence_timer_called_ = true; #endif this->silence_timer_canceled_.test_and_set(); if (this->silence_timer_) { detail::cancel_timer(*(this->silence_timer_)); } }); } protected: /// timer for session silence time out std::unique_ptr silence_timer_; /// Why use this flag, beacuase the ec param maybe zero when the timer callback is /// called after the timer cancel function has called already. std::atomic_flag silence_timer_canceled_; /// if there has no data transfer for a long time,the session will be disconnect std::chrono::steady_clock::duration silence_timeout_ = std::chrono::milliseconds(tcp_silence_timeout); #if defined(_DEBUG) || defined(DEBUG) bool is_stop_silence_timer_called_ = false; #endif }; } #endif // !__ASIO2_SILENCE_TIMER_COMPONENT_HPP__