condition_event_cp.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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_CONDITION_EVENT_COMPONENT_HPP__
  11. #define __ASIO2_CONDITION_EVENT_COMPONENT_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <cstddef>
  16. #include <map>
  17. #include <limits>
  18. #include <memory>
  19. #include <type_traits>
  20. #include <chrono>
  21. #include <mutex>
  22. #include <asio2/base/iopool.hpp>
  23. #include <asio2/base/detail/object.hpp>
  24. #include <asio2/base/detail/allocator.hpp>
  25. #include <asio2/util/spin_lock.hpp>
  26. namespace asio2::detail
  27. {
  28. template<class, class> class condition_event_cp;
  29. template<class, class> class rdc_call_cp_impl;
  30. }
  31. namespace asio2
  32. {
  33. class [[deprecated("Replace async_event with condition_event")]] async_event : public detail::object_t<async_event>
  34. {
  35. };
  36. class condition_event : public detail::object_t<condition_event>
  37. {
  38. template<class, class> friend class asio2::detail::condition_event_cp;
  39. template<class, class> friend class asio2::detail::rdc_call_cp_impl;
  40. public:
  41. explicit condition_event(std::shared_ptr<detail::io_t>& iot)
  42. : event_timer_io_(iot)
  43. {
  44. }
  45. ~condition_event() noexcept
  46. {
  47. }
  48. protected:
  49. template <typename WaitHandler>
  50. inline void async_wait(WaitHandler&& handler)
  51. {
  52. // when code run to here, it means that the io_context is started already,
  53. // then we set the flag to true, if the io_context is not started, we can't
  54. // set the flag to true, otherwise maybe cause crash.
  55. // eg :
  56. // asio2::udp_cast udp;
  57. // auto ptr = udp.post_condition_event([](){});
  58. // std::thread([ptr]() mutable
  59. // {
  60. // std::this_thread::sleep_for(std::chrono::seconds(5));
  61. // ptr->notify();
  62. // }).detach();
  63. // // udp.start(...); // udp.start is not called,
  64. // then the udp is destroyed, after 5 seconds, the code will run to ptr->notify,
  65. // then it cause crash...
  66. // when code run to here, the io_context must be not destroy.
  67. std::shared_ptr<detail::io_t> io_ptr = this->event_timer_io_.lock();
  68. if (!io_ptr)
  69. return;
  70. this->event_timer_ = std::make_unique<asio::steady_timer>(io_ptr->context());
  71. io_ptr->timers().emplace(this->event_timer_.get());
  72. // Setting expiration to infinity will cause handlers to
  73. // wait on the timer until cancelled.
  74. this->event_timer_->expires_after((std::chrono::nanoseconds::max)());
  75. // bind is used to adapt the user provided handler to the
  76. // timer's wait handler type requirement.
  77. // the "handler" has hold the derive_ptr already, so this lambda don't need hold it again.
  78. // this event_ptr (means self ptr) has holded by the map "condition_events_" already,
  79. // so this lambda don't need hold self ptr again.
  80. this->event_timer_->async_wait(
  81. [this, handler = std::forward<WaitHandler>(handler)](const error_code& ec) mutable
  82. {
  83. ASIO2_ASSERT((!ec) || ec == asio::error::operation_aborted);
  84. detail::ignore_unused(ec);
  85. std::shared_ptr<detail::io_t> io_ptr = this->event_timer_io_.lock();
  86. if (!io_ptr)
  87. return;
  88. io_ptr->timers().erase(this->event_timer_.get());
  89. handler();
  90. });
  91. }
  92. public:
  93. /**
  94. * @brief wakeup the waiting condition event.
  95. */
  96. inline void notify()
  97. {
  98. // must use dispatch, otherwise if use called post_condition_event, and then called
  99. // notify() immediately, the event will can't be notifyed.
  100. std::shared_ptr<detail::io_t> io_ptr = this->event_timer_io_.lock();
  101. if (!io_ptr)
  102. return;
  103. asio::dispatch(io_ptr->context(), [this, this_ptr = this->selfptr()]() mutable
  104. {
  105. detail::ignore_unused(this_ptr);
  106. detail::cancel_timer(*(this->event_timer_));
  107. });
  108. }
  109. protected:
  110. /// The io used for the timer. use weak_ptr, otherwise will cause circular reference.
  111. std::weak_ptr<detail::io_t> event_timer_io_;
  112. /// Used to implementing asynchronous condition event
  113. std::unique_ptr<asio::steady_timer> event_timer_;
  114. };
  115. }
  116. namespace asio2::detail
  117. {
  118. template<class derived_t, class args_t = void>
  119. class condition_event_cp
  120. {
  121. public:
  122. /**
  123. * @brief constructor
  124. */
  125. condition_event_cp() = default;
  126. /**
  127. * @brief destructor
  128. */
  129. ~condition_event_cp() = default;
  130. public:
  131. /**
  132. * @brief Post a asynchronous condition event to execution util the event is notifyed by user.
  133. * Before you call event_ptr->notify(); the event will not execute forever.
  134. * The function signature of the handler must be : void handler();
  135. */
  136. template<typename Function>
  137. inline std::shared_ptr<condition_event> post_condition_event(Function&& fn)
  138. {
  139. derived_t& derive = static_cast<derived_t&>(*this);
  140. std::shared_ptr<condition_event> event_ptr = std::make_shared<condition_event>(derive.io_);
  141. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  142. [this, this_ptr = derive.selfptr(), event_ptr, fn = std::forward<Function>(fn)]() mutable
  143. {
  144. condition_event* evt = event_ptr.get();
  145. this->condition_events_.emplace(evt, std::move(event_ptr));
  146. evt->async_wait([this, this_ptr = std::move(this_ptr), key = evt, fn = std::move(fn)]() mutable
  147. {
  148. fn();
  149. this->condition_events_.erase(key);
  150. });
  151. }));
  152. return event_ptr;
  153. }
  154. /**
  155. * @brief Notify all condition events to execute.
  156. */
  157. inline derived_t& notify_all_condition_events()
  158. {
  159. derived_t& derive = static_cast<derived_t&>(*this);
  160. // Make sure we run on the io_context thread
  161. asio::dispatch(derive.io_->context(), make_allocator(derive.wallocator(),
  162. [this, this_ptr = derive.selfptr()]() mutable
  163. {
  164. for (auto&[key, event_ptr] : this->condition_events_)
  165. {
  166. detail::ignore_unused(this_ptr, key);
  167. event_ptr->notify();
  168. }
  169. }));
  170. return derive;
  171. }
  172. protected:
  173. /// Used to exit the condition event when component is ready to stop.
  174. /// if user don't notify the event to execute, the io_context will
  175. /// block forever, so we need notify the condition event when component
  176. /// is ready to stop.
  177. std::map<condition_event*, std::shared_ptr<condition_event>> condition_events_;
  178. };
  179. }
  180. #endif // !__ASIO2_CONDITION_EVENT_COMPONENT_HPP__