silence_timer_cp.hpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_SILENCE_TIMER_COMPONENT_HPP__
  11. #define __ASIO2_SILENCE_TIMER_COMPONENT_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <chrono>
  16. #include <asio2/base/iopool.hpp>
  17. #include <asio2/base/log.hpp>
  18. namespace asio2::detail
  19. {
  20. template<class derived_t, class args_t>
  21. class silence_timer_cp
  22. {
  23. public:
  24. /**
  25. * @brief constructor
  26. */
  27. silence_timer_cp() = default;
  28. /**
  29. * @brief destructor
  30. */
  31. ~silence_timer_cp() = default;
  32. public:
  33. /**
  34. * @brief get silence timeout value
  35. */
  36. inline std::chrono::steady_clock::duration get_silence_timeout() const noexcept
  37. {
  38. return this->silence_timeout_;
  39. }
  40. /**
  41. * @brief set silence timeout value
  42. */
  43. template<class Rep, class Period>
  44. inline derived_t & set_silence_timeout(std::chrono::duration<Rep, Period> duration) noexcept
  45. {
  46. if (duration > std::chrono::duration_cast<
  47. std::chrono::duration<Rep, Period>>((std::chrono::steady_clock::duration::max)()))
  48. this->silence_timeout_ = (std::chrono::steady_clock::duration::max)();
  49. else
  50. this->silence_timeout_ = duration;
  51. return static_cast<derived_t&>(*this);
  52. }
  53. protected:
  54. template<class Rep, class Period>
  55. inline void _post_silence_timer(
  56. std::chrono::duration<Rep, Period> duration, std::shared_ptr<derived_t> this_ptr)
  57. {
  58. derived_t& derive = static_cast<derived_t&>(*this);
  59. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  60. [this, this_ptr = std::move(this_ptr), duration]() mutable
  61. {
  62. derived_t& derive = static_cast<derived_t&>(*this);
  63. #if defined(_DEBUG) || defined(DEBUG)
  64. ASIO2_ASSERT(this->is_stop_silence_timer_called_ == false);
  65. #endif
  66. // reset the "canceled" flag to false, see reconnect_timer_cp.hpp -> _make_reconnect_timer
  67. this->silence_timer_canceled_.clear();
  68. // start the timer of check silence timeout
  69. if (duration > std::chrono::duration<Rep, Period>::zero())
  70. {
  71. if (this->silence_timer_ == nullptr)
  72. {
  73. this->silence_timer_ = std::make_unique<asio::steady_timer>(derive.io_->context());
  74. }
  75. this->silence_timer_->expires_after(duration);
  76. this->silence_timer_->async_wait(
  77. [&derive, this_ptr = std::move(this_ptr)](const error_code & ec) mutable
  78. {
  79. derive._handle_silence_timer(ec, std::move(this_ptr));
  80. });
  81. }
  82. }));
  83. }
  84. inline void _handle_silence_timer(const error_code & ec, std::shared_ptr<derived_t> this_ptr)
  85. {
  86. derived_t& derive = static_cast<derived_t&>(*this);
  87. ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted);
  88. // ec maybe zero when timer_canceled_ is true.
  89. if (ec == asio::error::operation_aborted || this->silence_timer_canceled_.test_and_set())
  90. {
  91. this->silence_timer_.reset();
  92. return;
  93. }
  94. this->silence_timer_canceled_.clear();
  95. // silence duration seconds not exceed the silence timeout,post a timer
  96. // event agagin to avoid this session shared_ptr object disappear.
  97. std::chrono::system_clock::duration silence = derive.get_silence_duration();
  98. if (silence < this->silence_timeout_)
  99. {
  100. derive._post_silence_timer(this->silence_timeout_ - silence, std::move(this_ptr));
  101. }
  102. else
  103. {
  104. // silence timeout has elasped,but has't data trans,don't post
  105. // a timer event again,so this session, shared_ptr will disappear
  106. // and the object will be destroyed automatically after this handler returns.
  107. set_last_error(asio::error::timed_out);
  108. derive._do_disconnect(asio::error::timed_out, std::move(this_ptr));
  109. this->silence_timer_.reset();
  110. }
  111. }
  112. inline void _stop_silence_timer()
  113. {
  114. derived_t& derive = static_cast<derived_t&>(*this);
  115. derive.dispatch([this]() mutable
  116. {
  117. #if defined(_DEBUG) || defined(DEBUG)
  118. this->is_stop_silence_timer_called_ = true;
  119. #endif
  120. this->silence_timer_canceled_.test_and_set();
  121. if (this->silence_timer_)
  122. {
  123. detail::cancel_timer(*(this->silence_timer_));
  124. }
  125. });
  126. }
  127. protected:
  128. /// timer for session silence time out
  129. std::unique_ptr<asio::steady_timer> silence_timer_;
  130. /// Why use this flag, beacuase the ec param maybe zero when the timer callback is
  131. /// called after the timer cancel function has called already.
  132. std::atomic_flag silence_timer_canceled_;
  133. /// if there has no data transfer for a long time,the session will be disconnect
  134. std::chrono::steady_clock::duration silence_timeout_ = std::chrono::milliseconds(tcp_silence_timeout);
  135. #if defined(_DEBUG) || defined(DEBUG)
  136. bool is_stop_silence_timer_called_ = false;
  137. #endif
  138. };
  139. }
  140. #endif // !__ASIO2_SILENCE_TIMER_COMPONENT_HPP__