disconnect_cp.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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_DISCONNECT_COMPONENT_HPP__
  11. #define __ASIO2_DISCONNECT_COMPONENT_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <memory>
  16. #include <future>
  17. #include <utility>
  18. #include <string_view>
  19. #include <asio2/base/iopool.hpp>
  20. #include <asio2/base/listener.hpp>
  21. #include <asio2/base/impl/event_queue_cp.hpp>
  22. namespace asio2::detail
  23. {
  24. template<class derived_t, class args_t>
  25. class disconnect_cp
  26. {
  27. public:
  28. using self = disconnect_cp<derived_t, args_t>;
  29. public:
  30. /**
  31. * @brief constructor
  32. */
  33. disconnect_cp() noexcept {}
  34. /**
  35. * @brief destructor
  36. */
  37. ~disconnect_cp() = default;
  38. protected:
  39. template<typename E = defer_event<void, derived_t>>
  40. inline void _do_disconnect(
  41. const error_code& ec, std::shared_ptr<derived_t> this_ptr, E chain = defer_event<void, derived_t>{})
  42. {
  43. derived_t& derive = static_cast<derived_t&>(*this);
  44. derive.dispatch([&derive, ec, this_ptr = std::move(this_ptr), chain = std::move(chain)]() mutable
  45. {
  46. ASIO2_LOG_DEBUG("disconnect_cp::_do_disconnect: {} {}", ec.value(), ec.message());
  47. derive._do_shutdown(ec, std::move(this_ptr), std::move(chain));
  48. });
  49. }
  50. template<typename DeferEvent>
  51. inline void _post_disconnect(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  52. {
  53. derived_t& derive = static_cast<derived_t&>(*this);
  54. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  55. ASIO2_LOG_DEBUG("disconnect_cp::_post_disconnect: {} {}", ec.value(), ec.message());
  56. derive._handle_disconnect(ec, std::move(this_ptr), std::move(chain));
  57. }
  58. template<typename DeferEvent>
  59. inline void _handle_disconnect(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  60. {
  61. derived_t& derive = static_cast<derived_t&>(*this);
  62. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  63. // we should wait for the async read functions returned.
  64. // the reading flag will be always false of udp session.
  65. if (derive.reading_)
  66. {
  67. derive._make_readend_timer(ec, std::move(this_ptr), std::move(chain));
  68. }
  69. else
  70. {
  71. derive._handle_readend(ec, std::move(this_ptr), std::move(chain));
  72. }
  73. }
  74. template<typename DeferEvent>
  75. inline void _handle_readend(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  76. {
  77. derived_t& derive = static_cast<derived_t&>(*this);
  78. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  79. // at here the disconnect is completely finished.
  80. derive.disconnecting_ = false;
  81. if constexpr (args_t::is_session)
  82. {
  83. derive._do_stop(ec, std::move(this_ptr), std::move(chain));
  84. }
  85. else
  86. {
  87. detail::ignore_unused(ec, this_ptr, chain);
  88. }
  89. }
  90. protected:
  91. template<typename DeferEvent>
  92. inline void _make_readend_timer(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  93. {
  94. derived_t& derive = static_cast<derived_t&>(*this);
  95. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  96. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  97. [this, ec, this_ptr = std::move(this_ptr), chain = std::move(chain)]() mutable
  98. {
  99. derived_t& derive = static_cast<derived_t&>(*this);
  100. ASIO2_ASSERT(this->readend_timer_ == nullptr);
  101. if (this->readend_timer_)
  102. {
  103. this->readend_timer_->cancel();
  104. }
  105. this->readend_timer_ = std::make_shared<safe_timer>(derive.io_->context());
  106. derive._post_readend_timer(ec, std::move(this_ptr), std::move(chain), this->readend_timer_);
  107. }));
  108. }
  109. template<typename DeferEvent>
  110. inline void _post_readend_timer(
  111. const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain,
  112. std::shared_ptr<safe_timer> timer_ptr)
  113. {
  114. derived_t& derive = static_cast<derived_t&>(*this);
  115. // a new timer is maked, this is the prev timer, so return directly.
  116. if (timer_ptr.get() != this->readend_timer_.get())
  117. return;
  118. safe_timer* ptimer = timer_ptr.get();
  119. ptimer->timer.expires_after(derive.get_disconnect_timeout());
  120. ptimer->timer.async_wait(
  121. [&derive, ec, this_ptr = std::move(this_ptr), chain = std::move(chain), timer_ptr = std::move(timer_ptr)]
  122. (const error_code& timer_ec) mutable
  123. {
  124. derive._handle_readend_timer(
  125. timer_ec, ec, std::move(this_ptr), std::move(chain), std::move(timer_ptr));
  126. });
  127. }
  128. template<typename DeferEvent>
  129. inline void _handle_readend_timer(
  130. const error_code& timer_ec,
  131. const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain,
  132. std::shared_ptr<safe_timer> timer_ptr)
  133. {
  134. derived_t& derive = static_cast<derived_t&>(*this);
  135. ASIO2_ASSERT((!timer_ec) || timer_ec == asio::error::operation_aborted);
  136. // a new timer is maked, this is the prev timer, so return directly.
  137. if (timer_ptr.get() != this->readend_timer_.get())
  138. return;
  139. // member variable timer should't be empty
  140. if (!this->readend_timer_)
  141. {
  142. ASIO2_ASSERT(false);
  143. return;
  144. }
  145. // current timer is canceled by manual
  146. if (timer_ec == asio::error::operation_aborted || timer_ptr->canceled.test_and_set())
  147. {
  148. ASIO2_LOG_DEBUG("disconnect_cp::_handle_readend_timer: canceled");
  149. }
  150. // timeout
  151. else
  152. {
  153. ASIO2_LOG_DEBUG("disconnect_cp::_handle_readend_timer: timeout");
  154. }
  155. timer_ptr->canceled.clear();
  156. this->readend_timer_.reset();
  157. derive._handle_readend(ec, std::move(this_ptr), std::move(chain));
  158. }
  159. inline void _stop_readend_timer(std::shared_ptr<derived_t> this_ptr)
  160. {
  161. derived_t& derive = static_cast<derived_t&>(*this);
  162. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  163. [this, this_ptr = std::move(this_ptr)]() mutable
  164. {
  165. if (this->readend_timer_)
  166. {
  167. this->readend_timer_->cancel();
  168. }
  169. }));
  170. }
  171. public:
  172. /**
  173. * @brief get the disconnect timeout
  174. */
  175. inline std::chrono::steady_clock::duration get_disconnect_timeout() noexcept
  176. {
  177. return this->disconnect_timeout_;
  178. }
  179. /**
  180. * @brief set the disconnect timeout
  181. */
  182. template<class Rep, class Period>
  183. inline derived_t& set_disconnect_timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  184. {
  185. if (timeout > std::chrono::duration_cast<
  186. std::chrono::duration<Rep, Period>>((std::chrono::steady_clock::duration::max)()))
  187. this->disconnect_timeout_ = (std::chrono::steady_clock::duration::max)();
  188. else
  189. this->disconnect_timeout_ = timeout;
  190. return static_cast<derived_t&>(*this);
  191. }
  192. protected:
  193. std::chrono::steady_clock::duration disconnect_timeout_ = std::chrono::seconds(30);
  194. ///
  195. bool disconnecting_ = false;
  196. std::shared_ptr<safe_timer> readend_timer_;
  197. };
  198. }
  199. #endif // !__ASIO2_DISCONNECT_COMPONENT_HPP__