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