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