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