1 // 2 // detail/deadline_timer_service.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2017 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/io_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 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(boost::asio::io_context & io_context)67 deadline_timer_service(boost::asio::io_context& io_context) 68 : service_base<deadline_timer_service<Time_Traits> >(io_context), 69 scheduler_(boost::asio::use_service<timer_scheduler>(io_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 boost::system::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,boost::system::error_code & ec)133 std::size_t cancel(implementation_type& impl, boost::system::error_code& ec) 134 { 135 if (!impl.might_have_pending_waits) 136 { 137 ec = boost::system::error_code(); 138 return 0; 139 } 140 141 BOOST_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 = boost::system::error_code(); 147 return count; 148 } 149 150 // Cancels one asynchronous wait operation associated with the timer. cancel_one(implementation_type & impl,boost::system::error_code & ec)151 std::size_t cancel_one(implementation_type& impl, 152 boost::system::error_code& ec) 153 { 154 if (!impl.might_have_pending_waits) 155 { 156 ec = boost::system::error_code(); 157 return 0; 158 } 159 160 BOOST_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 = boost::system::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,boost::system::error_code & ec)190 std::size_t expires_at(implementation_type& impl, 191 const time_type& expiry_time, boost::system::error_code& ec) 192 { 193 std::size_t count = cancel(impl, ec); 194 impl.expiry = expiry_time; 195 ec = boost::system::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,boost::system::error_code & ec)200 std::size_t expires_after(implementation_type& impl, 201 const duration_type& expiry_time, boost::system::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,boost::system::error_code & ec)208 std::size_t expires_from_now(implementation_type& impl, 209 const duration_type& expiry_time, boost::system::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,boost::system::error_code & ec)216 void wait(implementation_type& impl, boost::system::error_code& ec) 217 { 218 time_type now = Time_Traits::now(); 219 ec = boost::system::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> async_wait(implementation_type & impl,Handler & handler)230 void async_wait(implementation_type& impl, Handler& handler) 231 { 232 // Allocate and construct an operation to wrap the handler. 233 typedef wait_handler<Handler> op; 234 typename op::ptr p = { boost::asio::detail::addressof(handler), 235 op::ptr::allocate(handler), 0 }; 236 p.p = new (p.v) op(handler); 237 238 impl.might_have_pending_waits = true; 239 240 BOOST_ASIO_HANDLER_CREATION((scheduler_.context(), 241 *p.p, "deadline_timer", &impl, 0, "async_wait")); 242 243 scheduler_.schedule_timer(timer_queue_, impl.expiry, impl.timer_data, p.p); 244 p.v = p.p = 0; 245 } 246 247 private: 248 // Helper function to wait given a duration type. The duration type should 249 // either be of type boost::posix_time::time_duration, or implement the 250 // required subset of its interface. 251 template <typename Duration> do_wait(const Duration & timeout,boost::system::error_code & ec)252 void do_wait(const Duration& timeout, boost::system::error_code& ec) 253 { 254 #if defined(BOOST_ASIO_WINDOWS_RUNTIME) 255 std::this_thread::sleep_for( 256 std::chrono::seconds(timeout.total_seconds()) 257 + std::chrono::microseconds(timeout.total_microseconds())); 258 ec = boost::system::error_code(); 259 #else // defined(BOOST_ASIO_WINDOWS_RUNTIME) 260 ::timeval tv; 261 tv.tv_sec = timeout.total_seconds(); 262 tv.tv_usec = timeout.total_microseconds() % 1000000; 263 socket_ops::select(0, 0, 0, 0, &tv, ec); 264 #endif // defined(BOOST_ASIO_WINDOWS_RUNTIME) 265 } 266 267 // The queue of timers. 268 timer_queue<Time_Traits> timer_queue_; 269 270 // The object that schedules and executes timers. Usually a reactor. 271 timer_scheduler& scheduler_; 272 }; 273 274 } // namespace detail 275 } // namespace asio 276 } // namespace boost 277 278 #include <boost/asio/detail/pop_options.hpp> 279 280 #endif // BOOST_ASIO_DETAIL_DEADLINE_TIMER_SERVICE_HPP 281