1 //
2 // io_context_strand.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_IO_CONTEXT_STRAND_HPP
12 #define BOOST_ASIO_IO_CONTEXT_STRAND_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 
20 #if !defined(BOOST_ASIO_NO_EXTENSIONS) \
21   && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
22 
23 #include <boost/asio/async_result.hpp>
24 #include <boost/asio/detail/handler_type_requirements.hpp>
25 #include <boost/asio/detail/strand_service.hpp>
26 #include <boost/asio/detail/wrapped_handler.hpp>
27 #include <boost/asio/io_context.hpp>
28 
29 #include <boost/asio/detail/push_options.hpp>
30 
31 namespace boost {
32 namespace asio {
33 
34 /// Provides serialised handler execution.
35 /**
36  * The io_context::strand class provides the ability to post and dispatch
37  * handlers with the guarantee that none of those handlers will execute
38  * concurrently.
39  *
40  * @par Order of handler invocation
41  * Given:
42  *
43  * @li a strand object @c s
44  *
45  * @li an object @c a meeting completion handler requirements
46  *
47  * @li an object @c a1 which is an arbitrary copy of @c a made by the
48  * implementation
49  *
50  * @li an object @c b meeting completion handler requirements
51  *
52  * @li an object @c b1 which is an arbitrary copy of @c b made by the
53  * implementation
54  *
55  * if any of the following conditions are true:
56  *
57  * @li @c s.post(a) happens-before @c s.post(b)
58  *
59  * @li @c s.post(a) happens-before @c s.dispatch(b), where the latter is
60  * performed outside the strand
61  *
62  * @li @c s.dispatch(a) happens-before @c s.post(b), where the former is
63  * performed outside the strand
64  *
65  * @li @c s.dispatch(a) happens-before @c s.dispatch(b), where both are
66  * performed outside the strand
67  *
68  * then @c asio_handler_invoke(a1, &a1) happens-before
69  * @c asio_handler_invoke(b1, &b1).
70  *
71  * Note that in the following case:
72  * @code async_op_1(..., s.wrap(a));
73  * async_op_2(..., s.wrap(b)); @endcode
74  * the completion of the first async operation will perform @c s.dispatch(a),
75  * and the second will perform @c s.dispatch(b), but the order in which those
76  * are performed is unspecified. That is, you cannot state whether one
77  * happens-before the other. Therefore none of the above conditions are met and
78  * no ordering guarantee is made.
79  *
80  * @note The implementation makes no guarantee that handlers posted or
81  * dispatched through different @c strand objects will be invoked concurrently.
82  *
83  * @par Thread Safety
84  * @e Distinct @e objects: Safe.@n
85  * @e Shared @e objects: Safe.
86  *
87  * @par Concepts:
88  * Dispatcher.
89  */
90 class io_context::strand
91 {
92 public:
93   /// Constructor.
94   /**
95    * Constructs the strand.
96    *
97    * @param io_context The io_context object that the strand will use to
98    * dispatch handlers that are ready to be run.
99    */
strand(boost::asio::io_context & io_context)100   explicit strand(boost::asio::io_context& io_context)
101     : service_(boost::asio::use_service<
102         boost::asio::detail::strand_service>(io_context))
103   {
104     service_.construct(impl_);
105   }
106 
107   /// Destructor.
108   /**
109    * Destroys a strand.
110    *
111    * Handlers posted through the strand that have not yet been invoked will
112    * still be dispatched in a way that meets the guarantee of non-concurrency.
113    */
~strand()114   ~strand()
115   {
116   }
117 
118   /// Obtain the underlying execution context.
context() const119   boost::asio::io_context& context() const BOOST_ASIO_NOEXCEPT
120   {
121     return service_.get_io_context();
122   }
123 
124   /// Inform the strand that it has some outstanding work to do.
125   /**
126    * The strand delegates this call to its underlying io_context.
127    */
on_work_started() const128   void on_work_started() const BOOST_ASIO_NOEXCEPT
129   {
130     context().get_executor().on_work_started();
131   }
132 
133   /// Inform the strand that some work is no longer outstanding.
134   /**
135    * The strand delegates this call to its underlying io_context.
136    */
on_work_finished() const137   void on_work_finished() const BOOST_ASIO_NOEXCEPT
138   {
139     context().get_executor().on_work_finished();
140   }
141 
142   /// Request the strand to invoke the given function object.
143   /**
144    * This function is used to ask the strand to execute the given function
145    * object on its underlying io_context. The function object will be executed
146    * inside this function if the strand is not otherwise busy and if the
147    * underlying io_context's executor's @c dispatch() function is also able to
148    * execute the function before returning.
149    *
150    * @param f The function object to be called. The executor will make
151    * a copy of the handler object as required. The function signature of the
152    * function object must be: @code void function(); @endcode
153    *
154    * @param a An allocator that may be used by the executor to allocate the
155    * internal storage needed for function invocation.
156    */
157   template <typename Function, typename Allocator>
dispatch(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const158   void dispatch(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
159   {
160     typename decay<Function>::type tmp(BOOST_ASIO_MOVE_CAST(Function)(f));
161     service_.dispatch(impl_, tmp);
162     (void)a;
163   }
164 
165 #if !defined(BOOST_ASIO_NO_DEPRECATED)
166   /// (Deprecated: Use boost::asio::dispatch().) Request the strand to invoke
167   /// the given handler.
168   /**
169    * This function is used to ask the strand to execute the given handler.
170    *
171    * The strand object guarantees that handlers posted or dispatched through
172    * the strand will not be executed concurrently. The handler may be executed
173    * inside this function if the guarantee can be met. If this function is
174    * called from within a handler that was posted or dispatched through the same
175    * strand, then the new handler will be executed immediately.
176    *
177    * The strand's guarantee is in addition to the guarantee provided by the
178    * underlying io_context. The io_context guarantees that the handler will only
179    * be called in a thread in which the io_context's run member function is
180    * currently being invoked.
181    *
182    * @param handler The handler to be called. The strand will make a copy of the
183    * handler object as required. The function signature of the handler must be:
184    * @code void handler(); @endcode
185    */
186   template <typename LegacyCompletionHandler>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(LegacyCompletionHandler,void ())187   BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(LegacyCompletionHandler, void ())
188   dispatch(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler) handler)
189   {
190     return async_initiate<LegacyCompletionHandler, void ()>(
191         initiate_dispatch(), handler, this);
192   }
193 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
194 
195   /// Request the strand to invoke the given function object.
196   /**
197    * This function is used to ask the executor to execute the given function
198    * object. The function object will never be executed inside this function.
199    * Instead, it will be scheduled to run by the underlying io_context.
200    *
201    * @param f The function object to be called. The executor will make
202    * a copy of the handler object as required. The function signature of the
203    * function object must be: @code void function(); @endcode
204    *
205    * @param a An allocator that may be used by the executor to allocate the
206    * internal storage needed for function invocation.
207    */
208   template <typename Function, typename Allocator>
post(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const209   void post(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
210   {
211     typename decay<Function>::type tmp(BOOST_ASIO_MOVE_CAST(Function)(f));
212     service_.post(impl_, tmp);
213     (void)a;
214   }
215 
216 #if !defined(BOOST_ASIO_NO_DEPRECATED)
217   /// (Deprecated: Use boost::asio::post().) Request the strand to invoke the
218   /// given handler and return immediately.
219   /**
220    * This function is used to ask the strand to execute the given handler, but
221    * without allowing the strand to call the handler from inside this function.
222    *
223    * The strand object guarantees that handlers posted or dispatched through
224    * the strand will not be executed concurrently. The strand's guarantee is in
225    * addition to the guarantee provided by the underlying io_context. The
226    * io_context guarantees that the handler will only be called in a thread in
227    * which the io_context's run member function is currently being invoked.
228    *
229    * @param handler The handler to be called. The strand will make a copy of the
230    * handler object as required. The function signature of the handler must be:
231    * @code void handler(); @endcode
232    */
233   template <typename LegacyCompletionHandler>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(LegacyCompletionHandler,void ())234   BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(LegacyCompletionHandler, void ())
235   post(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler) handler)
236   {
237     return async_initiate<LegacyCompletionHandler, void ()>(
238         initiate_post(), handler, this);
239   }
240 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
241 
242   /// Request the strand to invoke the given function object.
243   /**
244    * This function is used to ask the executor to execute the given function
245    * object. The function object will never be executed inside this function.
246    * Instead, it will be scheduled to run by the underlying io_context.
247    *
248    * @param f The function object to be called. The executor will make
249    * a copy of the handler object as required. The function signature of the
250    * function object must be: @code void function(); @endcode
251    *
252    * @param a An allocator that may be used by the executor to allocate the
253    * internal storage needed for function invocation.
254    */
255   template <typename Function, typename Allocator>
defer(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const256   void defer(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
257   {
258     typename decay<Function>::type tmp(BOOST_ASIO_MOVE_CAST(Function)(f));
259     service_.post(impl_, tmp);
260     (void)a;
261   }
262 
263 #if !defined(BOOST_ASIO_NO_DEPRECATED)
264   /// (Deprecated: Use boost::asio::bind_executor().) Create a new handler that
265   /// automatically dispatches the wrapped handler on the strand.
266   /**
267    * This function is used to create a new handler function object that, when
268    * invoked, will automatically pass the wrapped handler to the strand's
269    * dispatch function.
270    *
271    * @param handler The handler to be wrapped. The strand will make a copy of
272    * the handler object as required. The function signature of the handler must
273    * be: @code void handler(A1 a1, ... An an); @endcode
274    *
275    * @return A function object that, when invoked, passes the wrapped handler to
276    * the strand's dispatch function. Given a function object with the signature:
277    * @code R f(A1 a1, ... An an); @endcode
278    * If this function object is passed to the wrap function like so:
279    * @code strand.wrap(f); @endcode
280    * then the return value is a function object with the signature
281    * @code void g(A1 a1, ... An an); @endcode
282    * that, when invoked, executes code equivalent to:
283    * @code strand.dispatch(boost::bind(f, a1, ... an)); @endcode
284    */
285   template <typename Handler>
286 #if defined(GENERATING_DOCUMENTATION)
287   unspecified
288 #else
289   detail::wrapped_handler<strand, Handler, detail::is_continuation_if_running>
290 #endif
wrap(Handler handler)291   wrap(Handler handler)
292   {
293     return detail::wrapped_handler<io_context::strand, Handler,
294         detail::is_continuation_if_running>(*this, handler);
295   }
296 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
297 
298   /// Determine whether the strand is running in the current thread.
299   /**
300    * @return @c true if the current thread is executing a handler that was
301    * submitted to the strand using post(), dispatch() or wrap(). Otherwise
302    * returns @c false.
303    */
running_in_this_thread() const304   bool running_in_this_thread() const BOOST_ASIO_NOEXCEPT
305   {
306     return service_.running_in_this_thread(impl_);
307   }
308 
309   /// Compare two strands for equality.
310   /**
311    * Two strands are equal if they refer to the same ordered, non-concurrent
312    * state.
313    */
operator ==(const strand & a,const strand & b)314   friend bool operator==(const strand& a, const strand& b) BOOST_ASIO_NOEXCEPT
315   {
316     return a.impl_ == b.impl_;
317   }
318 
319   /// Compare two strands for inequality.
320   /**
321    * Two strands are equal if they refer to the same ordered, non-concurrent
322    * state.
323    */
operator !=(const strand & a,const strand & b)324   friend bool operator!=(const strand& a, const strand& b) BOOST_ASIO_NOEXCEPT
325   {
326     return a.impl_ != b.impl_;
327   }
328 
329 private:
330 #if !defined(BOOST_ASIO_NO_DEPRECATED)
331   struct initiate_dispatch
332   {
333     template <typename LegacyCompletionHandler>
operator ()boost::asio::io_context::strand::initiate_dispatch334     void operator()(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler) handler,
335         strand* self) const
336     {
337       // If you get an error on the following line it means that your
338       // handler does not meet the documented type requirements for a
339       // LegacyCompletionHandler.
340       BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(
341           LegacyCompletionHandler, handler) type_check;
342 
343       detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler);
344       self->service_.dispatch(self->impl_, handler2.value);
345     }
346   };
347 
348   struct initiate_post
349   {
350     template <typename LegacyCompletionHandler>
operator ()boost::asio::io_context::strand::initiate_post351     void operator()(BOOST_ASIO_MOVE_ARG(LegacyCompletionHandler) handler,
352         strand* self) const
353     {
354       // If you get an error on the following line it means that your
355       // handler does not meet the documented type requirements for a
356       // LegacyCompletionHandler.
357       BOOST_ASIO_LEGACY_COMPLETION_HANDLER_CHECK(
358           LegacyCompletionHandler, handler) type_check;
359 
360       detail::non_const_lvalue<LegacyCompletionHandler> handler2(handler);
361       self->service_.post(self->impl_, handler2.value);
362     }
363   };
364 #endif // !defined(BOOST_ASIO_NO_DEPRECATED)
365 
366   boost::asio::detail::strand_service& service_;
367   mutable boost::asio::detail::strand_service::implementation_type impl_;
368 };
369 
370 } // namespace asio
371 } // namespace boost
372 
373 #include <boost/asio/detail/pop_options.hpp>
374 
375 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
376        //   && !defined(BOOST_ASIO_NO_TS_EXECUTORS)
377 
378 #endif // BOOST_ASIO_IO_CONTEXT_STRAND_HPP
379