deadline_timer_service.hpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. //
  2. // detail/deadline_timer_service.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot 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 ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
  11. #define ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/config.hpp"
  16. #include <cstddef>
  17. #include "asio/associated_cancellation_slot.hpp"
  18. #include "asio/cancellation_type.hpp"
  19. #include "asio/error.hpp"
  20. #include "asio/execution_context.hpp"
  21. #include "asio/detail/bind_handler.hpp"
  22. #include "asio/detail/fenced_block.hpp"
  23. #include "asio/detail/memory.hpp"
  24. #include "asio/detail/noncopyable.hpp"
  25. #include "asio/detail/socket_ops.hpp"
  26. #include "asio/detail/socket_types.hpp"
  27. #include "asio/detail/timer_queue.hpp"
  28. #include "asio/detail/timer_queue_ptime.hpp"
  29. #include "asio/detail/timer_scheduler.hpp"
  30. #include "asio/detail/wait_handler.hpp"
  31. #include "asio/detail/wait_op.hpp"
  32. #if defined(ASIO_WINDOWS_RUNTIME)
  33. # include <chrono>
  34. # include <thread>
  35. #endif // defined(ASIO_WINDOWS_RUNTIME)
  36. #include "asio/detail/push_options.hpp"
  37. namespace asio {
  38. namespace detail {
  39. template <typename Time_Traits>
  40. class deadline_timer_service
  41. : public execution_context_service_base<deadline_timer_service<Time_Traits>>
  42. {
  43. public:
  44. // The time type.
  45. typedef typename Time_Traits::time_type time_type;
  46. // The duration type.
  47. typedef typename Time_Traits::duration_type duration_type;
  48. // The implementation type of the timer. This type is dependent on the
  49. // underlying implementation of the timer service.
  50. struct implementation_type
  51. : private asio::detail::noncopyable
  52. {
  53. time_type expiry;
  54. bool might_have_pending_waits;
  55. typename timer_queue<Time_Traits>::per_timer_data timer_data;
  56. };
  57. // Constructor.
  58. deadline_timer_service(execution_context& context)
  59. : execution_context_service_base<
  60. deadline_timer_service<Time_Traits>>(context),
  61. scheduler_(asio::use_service<timer_scheduler>(context))
  62. {
  63. scheduler_.init_task();
  64. scheduler_.add_timer_queue(timer_queue_);
  65. }
  66. // Destructor.
  67. ~deadline_timer_service()
  68. {
  69. scheduler_.remove_timer_queue(timer_queue_);
  70. }
  71. // Destroy all user-defined handler objects owned by the service.
  72. void shutdown()
  73. {
  74. }
  75. // Construct a new timer implementation.
  76. void construct(implementation_type& impl)
  77. {
  78. impl.expiry = time_type();
  79. impl.might_have_pending_waits = false;
  80. }
  81. // Destroy a timer implementation.
  82. void destroy(implementation_type& impl)
  83. {
  84. asio::error_code ec;
  85. cancel(impl, ec);
  86. }
  87. // Move-construct a new timer implementation.
  88. void move_construct(implementation_type& impl,
  89. implementation_type& other_impl)
  90. {
  91. scheduler_.move_timer(timer_queue_, impl.timer_data, other_impl.timer_data);
  92. impl.expiry = other_impl.expiry;
  93. other_impl.expiry = time_type();
  94. impl.might_have_pending_waits = other_impl.might_have_pending_waits;
  95. other_impl.might_have_pending_waits = false;
  96. }
  97. // Move-assign from another timer implementation.
  98. void move_assign(implementation_type& impl,
  99. deadline_timer_service& other_service,
  100. implementation_type& other_impl)
  101. {
  102. if (this != &other_service)
  103. if (impl.might_have_pending_waits)
  104. scheduler_.cancel_timer(timer_queue_, impl.timer_data);
  105. other_service.scheduler_.move_timer(other_service.timer_queue_,
  106. impl.timer_data, other_impl.timer_data);
  107. impl.expiry = other_impl.expiry;
  108. other_impl.expiry = time_type();
  109. impl.might_have_pending_waits = other_impl.might_have_pending_waits;
  110. other_impl.might_have_pending_waits = false;
  111. }
  112. // Move-construct a new timer implementation.
  113. void converting_move_construct(implementation_type& impl,
  114. deadline_timer_service&, implementation_type& other_impl)
  115. {
  116. move_construct(impl, other_impl);
  117. }
  118. // Move-assign from another timer implementation.
  119. void converting_move_assign(implementation_type& impl,
  120. deadline_timer_service& other_service,
  121. implementation_type& other_impl)
  122. {
  123. move_assign(impl, other_service, other_impl);
  124. }
  125. // Cancel any asynchronous wait operations associated with the timer.
  126. std::size_t cancel(implementation_type& impl, asio::error_code& ec)
  127. {
  128. if (!impl.might_have_pending_waits)
  129. {
  130. ec = asio::error_code();
  131. return 0;
  132. }
  133. ASIO_HANDLER_OPERATION((scheduler_.context(),
  134. "deadline_timer", &impl, 0, "cancel"));
  135. std::size_t count = scheduler_.cancel_timer(timer_queue_, impl.timer_data);
  136. impl.might_have_pending_waits = false;
  137. ec = asio::error_code();
  138. return count;
  139. }
  140. // Cancels one asynchronous wait operation associated with the timer.
  141. std::size_t cancel_one(implementation_type& impl,
  142. asio::error_code& ec)
  143. {
  144. if (!impl.might_have_pending_waits)
  145. {
  146. ec = asio::error_code();
  147. return 0;
  148. }
  149. ASIO_HANDLER_OPERATION((scheduler_.context(),
  150. "deadline_timer", &impl, 0, "cancel_one"));
  151. std::size_t count = scheduler_.cancel_timer(
  152. timer_queue_, impl.timer_data, 1);
  153. if (count == 0)
  154. impl.might_have_pending_waits = false;
  155. ec = asio::error_code();
  156. return count;
  157. }
  158. // Get the expiry time for the timer as an absolute time.
  159. time_type expiry(const implementation_type& impl) const
  160. {
  161. return impl.expiry;
  162. }
  163. // Get the expiry time for the timer as an absolute time.
  164. time_type expires_at(const implementation_type& impl) const
  165. {
  166. return impl.expiry;
  167. }
  168. // Get the expiry time for the timer relative to now.
  169. duration_type expires_from_now(const implementation_type& impl) const
  170. {
  171. return Time_Traits::subtract(this->expiry(impl), Time_Traits::now());
  172. }
  173. // Set the expiry time for the timer as an absolute time.
  174. std::size_t expires_at(implementation_type& impl,
  175. const time_type& expiry_time, asio::error_code& ec)
  176. {
  177. std::size_t count = cancel(impl, ec);
  178. impl.expiry = expiry_time;
  179. ec = asio::error_code();
  180. return count;
  181. }
  182. // Set the expiry time for the timer relative to now.
  183. std::size_t expires_after(implementation_type& impl,
  184. const duration_type& expiry_time, asio::error_code& ec)
  185. {
  186. return expires_at(impl,
  187. Time_Traits::add(Time_Traits::now(), expiry_time), ec);
  188. }
  189. // Set the expiry time for the timer relative to now.
  190. std::size_t expires_from_now(implementation_type& impl,
  191. const duration_type& expiry_time, asio::error_code& ec)
  192. {
  193. return expires_at(impl,
  194. Time_Traits::add(Time_Traits::now(), expiry_time), ec);
  195. }
  196. // Perform a blocking wait on the timer.
  197. void wait(implementation_type& impl, asio::error_code& ec)
  198. {
  199. time_type now = Time_Traits::now();
  200. ec = asio::error_code();
  201. while (Time_Traits::less_than(now, impl.expiry) && !ec)
  202. {
  203. this->do_wait(Time_Traits::to_posix_duration(
  204. Time_Traits::subtract(impl.expiry, now)), ec);
  205. now = Time_Traits::now();
  206. }
  207. }
  208. // Start an asynchronous wait on the timer.
  209. template <typename Handler, typename IoExecutor>
  210. void async_wait(implementation_type& impl,
  211. Handler& handler, const IoExecutor& io_ex)
  212. {
  213. associated_cancellation_slot_t<Handler> slot
  214. = asio::get_associated_cancellation_slot(handler);
  215. // Allocate and construct an operation to wrap the handler.
  216. typedef wait_handler<Handler, IoExecutor> op;
  217. typename op::ptr p = { asio::detail::addressof(handler),
  218. op::ptr::allocate(handler), 0 };
  219. p.p = new (p.v) op(handler, io_ex);
  220. // Optionally register for per-operation cancellation.
  221. if (slot.is_connected())
  222. {
  223. p.p->cancellation_key_ =
  224. &slot.template emplace<op_cancellation>(this, &impl.timer_data);
  225. }
  226. impl.might_have_pending_waits = true;
  227. ASIO_HANDLER_CREATION((scheduler_.context(),
  228. *p.p, "deadline_timer", &impl, 0, "async_wait"));
  229. scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p);
  230. p.v = p.p = 0;
  231. }
  232. private:
  233. // Helper function to wait given a duration type. The duration type should
  234. // either be of type boost::posix_time::time_duration, or implement the
  235. // required subset of its interface.
  236. template <typename Duration>
  237. void do_wait(const Duration& timeout, asio::error_code& ec)
  238. {
  239. #if defined(ASIO_WINDOWS_RUNTIME)
  240. std::this_thread::sleep_for(
  241. std::chrono::seconds(timeout.total_seconds())
  242. + std::chrono::microseconds(timeout.total_microseconds()));
  243. ec = asio::error_code();
  244. #else // defined(ASIO_WINDOWS_RUNTIME)
  245. ::timeval tv;
  246. tv.tv_sec = timeout.total_seconds();
  247. tv.tv_usec = timeout.total_microseconds() % 1000000;
  248. socket_ops::select(0, 0, 0, 0, &tv, ec);
  249. #endif // defined(ASIO_WINDOWS_RUNTIME)
  250. }
  251. // Helper class used to implement per-operation cancellation.
  252. class op_cancellation
  253. {
  254. public:
  255. op_cancellation(deadline_timer_service* s,
  256. typename timer_queue<Time_Traits>::per_timer_data* p)
  257. : service_(s),
  258. timer_data_(p)
  259. {
  260. }
  261. void operator()(cancellation_type_t type)
  262. {
  263. if (!!(type &
  264. (cancellation_type::terminal
  265. | cancellation_type::partial
  266. | cancellation_type::total)))
  267. {
  268. service_->scheduler_.cancel_timer_by_key(
  269. service_->timer_queue_, timer_data_, this);
  270. }
  271. }
  272. private:
  273. deadline_timer_service* service_;
  274. typename timer_queue<Time_Traits>::per_timer_data* timer_data_;
  275. };
  276. // The queue of timers.
  277. timer_queue<Time_Traits> timer_queue_;
  278. // The object that schedules and executes timers. Usually a reactor.
  279. timer_scheduler& scheduler_;
  280. };
  281. } // namespace detail
  282. } // namespace asio
  283. #include "asio/detail/pop_options.hpp"
  284. #endif // ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP