reconnect_timer_cp.hpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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_RECONNECT_TIMER_COMPONENT_HPP__
  11. #define __ASIO2_RECONNECT_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 reconnect_timer_cp
  22. {
  23. public:
  24. /**
  25. * @brief constructor
  26. */
  27. reconnect_timer_cp() = default;
  28. /**
  29. * @brief destructor
  30. */
  31. ~reconnect_timer_cp() = default;
  32. public:
  33. /**
  34. * @brief set the option of whether auto reconnect when disconnected, same as set_auto_reconnect
  35. * @param enable - whether reconnect or not
  36. */
  37. template<typename = void>
  38. inline derived_t& auto_reconnect(bool enable) noexcept
  39. {
  40. return this->set_auto_reconnect(enable);
  41. }
  42. /**
  43. * @brief set the option of whether auto reconnect when disconnected, same as set_auto_reconnect
  44. * @param enable - whether reconnect or not
  45. * @param delay - how long is the delay before reconnecting, when enalbe is
  46. * false, the delay param is ignored
  47. */
  48. template<class Rep, class Period>
  49. inline derived_t& auto_reconnect(bool enable, std::chrono::duration<Rep, Period> delay) noexcept
  50. {
  51. return this->set_auto_reconnect(enable, std::move(delay));
  52. }
  53. /**
  54. * @brief set the option of whether auto reconnect when disconnected
  55. * @param enable - whether reconnect or not
  56. */
  57. template<typename = void>
  58. inline derived_t& set_auto_reconnect(bool enable) noexcept
  59. {
  60. this->reconnect_enable_ = enable;
  61. return static_cast<derived_t&>(*this);
  62. }
  63. /**
  64. * @brief set the option of whether auto reconnect when disconnected
  65. * @param enable - whether reconnect or not
  66. * @param delay - how long is the delay before reconnecting, when enalbe is
  67. * false, the delay param is ignored
  68. */
  69. template<class Rep, class Period>
  70. inline derived_t& set_auto_reconnect(bool enable, std::chrono::duration<Rep, Period> delay) noexcept
  71. {
  72. this->reconnect_enable_ = enable;
  73. if (delay > std::chrono::duration_cast<
  74. std::chrono::duration<Rep, Period>>((std::chrono::steady_clock::duration::max)()))
  75. this->reconnect_delay_ = (std::chrono::steady_clock::duration::max)();
  76. else
  77. this->reconnect_delay_ = delay;
  78. return static_cast<derived_t&>(*this);
  79. }
  80. /**
  81. * @brief get whether auto reconnect is enabled or not
  82. */
  83. template<typename = void>
  84. inline bool is_auto_reconnect() const noexcept
  85. {
  86. return this->reconnect_enable_;
  87. }
  88. /**
  89. * @brief get the delay before reconnecting, when enalbe is
  90. */
  91. template<typename = void>
  92. inline std::chrono::steady_clock::duration get_auto_reconnect_delay() const noexcept
  93. {
  94. return this->reconnect_delay_;
  95. }
  96. protected:
  97. template<class C>
  98. inline void _make_reconnect_timer(std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs)
  99. {
  100. derived_t& derive = static_cast<derived_t&>(*this);
  101. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  102. [this, this_ptr = std::move(this_ptr), ecs = std::move(ecs)]() mutable
  103. {
  104. derived_t& derive = static_cast<derived_t&>(*this);
  105. if (this->reconnect_timer_)
  106. {
  107. this->reconnect_timer_->cancel();
  108. }
  109. this->reconnect_timer_ = std::make_shared<safe_timer>(derive.io_->context());
  110. derive._post_reconnect_timer(std::move(this_ptr), std::move(ecs),
  111. this->reconnect_timer_, (std::chrono::nanoseconds::max)()); // 292 yeas
  112. }));
  113. }
  114. template<class Rep, class Period, class C>
  115. inline void _post_reconnect_timer(
  116. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs,
  117. std::shared_ptr<safe_timer> timer_ptr, std::chrono::duration<Rep, Period> delay)
  118. {
  119. derived_t& derive = static_cast<derived_t&>(*this);
  120. #if defined(_DEBUG) || defined(DEBUG)
  121. this->is_post_reconnect_timer_called_ = true;
  122. ASIO2_ASSERT(this->is_stop_reconnect_timer_called_ == false);
  123. #endif
  124. // When goto timer callback, and execute the reconnect operation :
  125. // call _start_connect -> _make_reconnect_timer -> a new timer is maked, and
  126. // the prev timer will be canceled, then call _post_reconnect_timer, the prev
  127. // timer will be enqueue to, this will cause the prev timer never be exit.
  128. // so we check if timer_ptr is not equal the member variable reconnect_timer_,
  129. // don't call async_wait again.
  130. if (timer_ptr.get() != this->reconnect_timer_.get())
  131. return;
  132. safe_timer* ptimer = timer_ptr.get();
  133. ptimer->timer.expires_after(delay);
  134. ptimer->timer.async_wait(
  135. [&derive, this_ptr = std::move(this_ptr), ecs = std::move(ecs), timer_ptr = std::move(timer_ptr)]
  136. (const error_code & ec) mutable
  137. {
  138. derive._handle_reconnect_timer(ec, std::move(this_ptr), std::move(ecs), std::move(timer_ptr));
  139. });
  140. }
  141. template<class C>
  142. inline void _handle_reconnect_timer(const error_code& ec,
  143. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, std::shared_ptr<safe_timer> timer_ptr)
  144. {
  145. derived_t& derive = static_cast<derived_t&>(*this);
  146. ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted);
  147. // a new timer is maked, this is the prev timer, so return directly.
  148. if (timer_ptr.get() != this->reconnect_timer_.get())
  149. return;
  150. // member variable timer should't be empty
  151. if (!this->reconnect_timer_)
  152. {
  153. ASIO2_ASSERT(false);
  154. return;
  155. }
  156. // current reconnect timer is canceled, so return directly
  157. if (timer_ptr->canceled.test_and_set())
  158. {
  159. this->reconnect_timer_.reset();
  160. return;
  161. }
  162. timer_ptr->canceled.clear();
  163. if (ec == asio::error::operation_aborted)
  164. {
  165. derive._post_reconnect_timer(
  166. std::move(this_ptr), std::move(ecs), std::move(timer_ptr), this->reconnect_delay_);
  167. }
  168. else
  169. {
  170. if (this->reconnect_enable_)
  171. {
  172. derive.push_event(
  173. [&derive, this_ptr, ecs, timer_ptr](event_queue_guard<derived_t> g) mutable
  174. {
  175. if (timer_ptr->canceled.test_and_set())
  176. return;
  177. timer_ptr->canceled.clear();
  178. state_t expected = state_t::stopped;
  179. if (derive.state_.compare_exchange_strong(expected, state_t::starting))
  180. {
  181. derive.template _start_connect<true>(
  182. std::move(this_ptr), std::move(ecs), defer_event(std::move(g)));
  183. }
  184. });
  185. }
  186. derive._post_reconnect_timer(std::move(this_ptr), std::move(ecs),
  187. std::move(timer_ptr), (std::chrono::nanoseconds::max)()); // 292 yeas
  188. }
  189. }
  190. inline void _stop_reconnect_timer()
  191. {
  192. derived_t& derive = static_cast<derived_t&>(*this);
  193. derive.dispatch([this]() mutable
  194. {
  195. #if defined(_DEBUG) || defined(DEBUG)
  196. this->is_stop_reconnect_timer_called_ = true;
  197. #endif
  198. if (this->reconnect_timer_)
  199. {
  200. this->reconnect_timer_->cancel();
  201. }
  202. });
  203. }
  204. inline void _wake_reconnect_timer()
  205. {
  206. derived_t& derive = static_cast<derived_t&>(*this);
  207. derive.dispatch([this]() mutable
  208. {
  209. #if defined(_DEBUG) || defined(DEBUG)
  210. ASIO2_ASSERT(this->is_post_reconnect_timer_called_ == true);
  211. #endif
  212. if (this->reconnect_enable_)
  213. {
  214. ASIO2_ASSERT(this->reconnect_timer_);
  215. if (this->reconnect_timer_)
  216. {
  217. detail::cancel_timer(this->reconnect_timer_->timer);
  218. }
  219. }
  220. });
  221. }
  222. protected:
  223. /// timer for client reconnect
  224. std::shared_ptr<safe_timer> reconnect_timer_;
  225. /// if there has no data transfer for a long time,the session will be disconnect
  226. std::chrono::steady_clock::duration reconnect_delay_ = std::chrono::seconds(1);
  227. /// flag of whether reconnect when disconnect
  228. bool reconnect_enable_ = true;
  229. #if defined(_DEBUG) || defined(DEBUG)
  230. bool is_stop_reconnect_timer_called_ = false;
  231. bool is_post_reconnect_timer_called_ = false;
  232. #endif
  233. };
  234. }
  235. #endif // !__ASIO2_RECONNECT_TIMER_COMPONENT_HPP__