1 //
2 // 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_STRAND_HPP
12 #define BOOST_ASIO_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 #include <boost/asio/detail/strand_executor_service.hpp>
20 #include <boost/asio/detail/type_traits.hpp>
21 #include <boost/asio/execution/blocking.hpp>
22 #include <boost/asio/execution/executor.hpp>
23 #include <boost/asio/is_executor.hpp>
24 
25 #include <boost/asio/detail/push_options.hpp>
26 
27 namespace boost {
28 namespace asio {
29 
30 /// Provides serialised function invocation for any executor type.
31 template <typename Executor>
32 class strand
33 {
34 public:
35   /// The type of the underlying executor.
36   typedef Executor inner_executor_type;
37 
38   /// Default constructor.
39   /**
40    * This constructor is only valid if the underlying executor type is default
41    * constructible.
42    */
strand()43   strand()
44     : executor_(),
45       impl_(strand::create_implementation(executor_))
46   {
47   }
48 
49   /// Construct a strand for the specified executor.
50   template <typename Executor1>
strand(const Executor1 & e,typename constraint<conditional<!is_same<Executor1,strand>::value,is_convertible<Executor1,Executor>,false_type>::type::value>::type=0)51   explicit strand(const Executor1& e,
52       typename constraint<
53         conditional<
54           !is_same<Executor1, strand>::value,
55           is_convertible<Executor1, Executor>,
56           false_type
57         >::type::value
58       >::type = 0)
59     : executor_(e),
60       impl_(strand::create_implementation(executor_))
61   {
62   }
63 
64   /// Copy constructor.
strand(const strand & other)65   strand(const strand& other) BOOST_ASIO_NOEXCEPT
66     : executor_(other.executor_),
67       impl_(other.impl_)
68   {
69   }
70 
71   /// Converting constructor.
72   /**
73    * This constructor is only valid if the @c OtherExecutor type is convertible
74    * to @c Executor.
75    */
76   template <class OtherExecutor>
strand(const strand<OtherExecutor> & other)77   strand(
78       const strand<OtherExecutor>& other) BOOST_ASIO_NOEXCEPT
79     : executor_(other.executor_),
80       impl_(other.impl_)
81   {
82   }
83 
84   /// Assignment operator.
operator =(const strand & other)85   strand& operator=(const strand& other) BOOST_ASIO_NOEXCEPT
86   {
87     executor_ = other.executor_;
88     impl_ = other.impl_;
89     return *this;
90   }
91 
92   /// Converting assignment operator.
93   /**
94    * This assignment operator is only valid if the @c OtherExecutor type is
95    * convertible to @c Executor.
96    */
97   template <class OtherExecutor>
operator =(const strand<OtherExecutor> & other)98   strand& operator=(
99       const strand<OtherExecutor>& other) BOOST_ASIO_NOEXCEPT
100   {
101     executor_ = other.executor_;
102     impl_ = other.impl_;
103     return *this;
104   }
105 
106 #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
107   /// Move constructor.
strand(strand && other)108   strand(strand&& other) BOOST_ASIO_NOEXCEPT
109     : executor_(BOOST_ASIO_MOVE_CAST(Executor)(other.executor_)),
110       impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_))
111   {
112   }
113 
114   /// Converting move constructor.
115   /**
116    * This constructor is only valid if the @c OtherExecutor type is convertible
117    * to @c Executor.
118    */
119   template <class OtherExecutor>
strand(strand<OtherExecutor> && other)120   strand(strand<OtherExecutor>&& other) BOOST_ASIO_NOEXCEPT
121     : executor_(BOOST_ASIO_MOVE_CAST(OtherExecutor)(other.executor_)),
122       impl_(BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_))
123   {
124   }
125 
126   /// Move assignment operator.
operator =(strand && other)127   strand& operator=(strand&& other) BOOST_ASIO_NOEXCEPT
128   {
129     executor_ = BOOST_ASIO_MOVE_CAST(Executor)(other.executor_);
130     impl_ = BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_);
131     return *this;
132   }
133 
134   /// Converting move assignment operator.
135   /**
136    * This assignment operator is only valid if the @c OtherExecutor type is
137    * convertible to @c Executor.
138    */
139   template <class OtherExecutor>
operator =(strand<OtherExecutor> && other)140   strand& operator=(strand<OtherExecutor>&& other) BOOST_ASIO_NOEXCEPT
141   {
142     executor_ = BOOST_ASIO_MOVE_CAST(OtherExecutor)(other.executor_);
143     impl_ = BOOST_ASIO_MOVE_CAST(implementation_type)(other.impl_);
144     return *this;
145   }
146 #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
147 
148   /// Destructor.
~strand()149   ~strand() BOOST_ASIO_NOEXCEPT
150   {
151   }
152 
153   /// Obtain the underlying executor.
get_inner_executor() const154   inner_executor_type get_inner_executor() const BOOST_ASIO_NOEXCEPT
155   {
156     return executor_;
157   }
158 
159   /// Forward a query to the underlying executor.
160   /**
161    * Do not call this function directly. It is intended for use with the
162    * boost::asio::query customisation point.
163    *
164    * For example:
165    * @code boost::asio::strand<my_executor_type> ex = ...;
166    * if (boost::asio::query(ex, boost::asio::execution::blocking)
167    *       == boost::asio::execution::blocking.never)
168    *   ... @endcode
169    */
170   template <typename Property>
171   typename constraint<
172     can_query<const Executor&, Property>::value,
173     typename conditional<
174       is_convertible<Property, execution::blocking_t>::value,
175       execution::blocking_t,
176       typename query_result<const Executor&, Property>::type
177     >::type
query(const Property & p) const178   >::type query(const Property& p) const
179     BOOST_ASIO_NOEXCEPT_IF((
180       is_nothrow_query<const Executor&, Property>::value))
181   {
182     return this->query_helper(
183         is_convertible<Property, execution::blocking_t>(), p);
184   }
185 
186   /// Forward a requirement to the underlying executor.
187   /**
188    * Do not call this function directly. It is intended for use with the
189    * boost::asio::require customisation point.
190    *
191    * For example:
192    * @code boost::asio::strand<my_executor_type> ex1 = ...;
193    * auto ex2 = boost::asio::require(ex1,
194    *     boost::asio::execution::blocking.never); @endcode
195    */
196   template <typename Property>
197   typename constraint<
198     can_require<const Executor&, Property>::value
199       && !is_convertible<Property, execution::blocking_t::always_t>::value,
200     strand<typename decay<
201       typename require_result<const Executor&, Property>::type
202     >::type>
require(const Property & p) const203   >::type require(const Property& p) const
204     BOOST_ASIO_NOEXCEPT_IF((
205       is_nothrow_require<const Executor&, Property>::value))
206   {
207     return strand<typename decay<
208       typename require_result<const Executor&, Property>::type
209         >::type>(boost::asio::require(executor_, p), impl_);
210   }
211 
212   /// Forward a preference to the underlying executor.
213   /**
214    * Do not call this function directly. It is intended for use with the
215    * boost::asio::prefer customisation point.
216    *
217    * For example:
218    * @code boost::asio::strand<my_executor_type> ex1 = ...;
219    * auto ex2 = boost::asio::prefer(ex1,
220    *     boost::asio::execution::blocking.never); @endcode
221    */
222   template <typename Property>
223   typename constraint<
224     can_prefer<const Executor&, Property>::value
225       && !is_convertible<Property, execution::blocking_t::always_t>::value,
226     strand<typename decay<
227       typename prefer_result<const Executor&, Property>::type
228     >::type>
prefer(const Property & p) const229   >::type prefer(const Property& p) const
230     BOOST_ASIO_NOEXCEPT_IF((
231       is_nothrow_prefer<const Executor&, Property>::value))
232   {
233     return strand<typename decay<
234       typename prefer_result<const Executor&, Property>::type
235         >::type>(boost::asio::prefer(executor_, p), impl_);
236   }
237 
238 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
239   /// Obtain the underlying execution context.
context() const240   execution_context& context() const BOOST_ASIO_NOEXCEPT
241   {
242     return executor_.context();
243   }
244 
245   /// Inform the strand that it has some outstanding work to do.
246   /**
247    * The strand delegates this call to its underlying executor.
248    */
on_work_started() const249   void on_work_started() const BOOST_ASIO_NOEXCEPT
250   {
251     executor_.on_work_started();
252   }
253 
254   /// Inform the strand that some work is no longer outstanding.
255   /**
256    * The strand delegates this call to its underlying executor.
257    */
on_work_finished() const258   void on_work_finished() const BOOST_ASIO_NOEXCEPT
259   {
260     executor_.on_work_finished();
261   }
262 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
263 
264   /// Request the strand to invoke the given function object.
265   /**
266    * Do not call this function directly. It is intended for use with the
267    * execution::execute customisation point.
268    *
269    * For example:
270    * @code boost::asio::strand<my_executor_type> ex = ...;
271    * execution::execute(ex, my_function_object); @endcode
272    *
273    * This function is used to ask the strand to execute the given function
274    * object on its underlying executor. The function object will be executed
275    * according to the properties of the underlying executor.
276    *
277    * @param f The function object to be called. The executor will make
278    * a copy of the handler object as required. The function signature of the
279    * function object must be: @code void function(); @endcode
280    */
281   template <typename Function>
282   typename constraint<
283     execution::can_execute<const Executor&, Function>::value,
284     void
execute(BOOST_ASIO_MOVE_ARG (Function)f) const285   >::type execute(BOOST_ASIO_MOVE_ARG(Function) f) const
286   {
287     detail::strand_executor_service::execute(impl_,
288         executor_, BOOST_ASIO_MOVE_CAST(Function)(f));
289   }
290 
291 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
292   /// Request the strand to invoke the given function object.
293   /**
294    * This function is used to ask the strand to execute the given function
295    * object on its underlying executor. The function object will be executed
296    * inside this function if the strand is not otherwise busy and if the
297    * underlying executor's @c dispatch() function is also able to execute the
298    * function before returning.
299    *
300    * @param f The function object to be called. The executor will make
301    * a copy of the handler object as required. The function signature of the
302    * function object must be: @code void function(); @endcode
303    *
304    * @param a An allocator that may be used by the executor to allocate the
305    * internal storage needed for function invocation.
306    */
307   template <typename Function, typename Allocator>
dispatch(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const308   void dispatch(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
309   {
310     detail::strand_executor_service::dispatch(impl_,
311         executor_, BOOST_ASIO_MOVE_CAST(Function)(f), a);
312   }
313 
314   /// Request the strand to invoke the given function object.
315   /**
316    * This function is used to ask the executor to execute the given function
317    * object. The function object will never be executed inside this function.
318    * Instead, it will be scheduled by the underlying executor's defer function.
319    *
320    * @param f The function object to be called. The executor will make
321    * a copy of the handler object as required. The function signature of the
322    * function object must be: @code void function(); @endcode
323    *
324    * @param a An allocator that may be used by the executor to allocate the
325    * internal storage needed for function invocation.
326    */
327   template <typename Function, typename Allocator>
post(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const328   void post(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
329   {
330     detail::strand_executor_service::post(impl_,
331         executor_, BOOST_ASIO_MOVE_CAST(Function)(f), a);
332   }
333 
334   /// Request the strand to invoke the given function object.
335   /**
336    * This function is used to ask the executor to execute the given function
337    * object. The function object will never be executed inside this function.
338    * Instead, it will be scheduled by the underlying executor's defer function.
339    *
340    * @param f The function object to be called. The executor will make
341    * a copy of the handler object as required. The function signature of the
342    * function object must be: @code void function(); @endcode
343    *
344    * @param a An allocator that may be used by the executor to allocate the
345    * internal storage needed for function invocation.
346    */
347   template <typename Function, typename Allocator>
defer(BOOST_ASIO_MOVE_ARG (Function)f,const Allocator & a) const348   void defer(BOOST_ASIO_MOVE_ARG(Function) f, const Allocator& a) const
349   {
350     detail::strand_executor_service::defer(impl_,
351         executor_, BOOST_ASIO_MOVE_CAST(Function)(f), a);
352   }
353 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
354 
355   /// Determine whether the strand is running in the current thread.
356   /**
357    * @return @c true if the current thread is executing a function that was
358    * submitted to the strand using post(), dispatch() or defer(). Otherwise
359    * returns @c false.
360    */
running_in_this_thread() const361   bool running_in_this_thread() const BOOST_ASIO_NOEXCEPT
362   {
363     return detail::strand_executor_service::running_in_this_thread(impl_);
364   }
365 
366   /// Compare two strands for equality.
367   /**
368    * Two strands are equal if they refer to the same ordered, non-concurrent
369    * state.
370    */
operator ==(const strand & a,const strand & b)371   friend bool operator==(const strand& a, const strand& b) BOOST_ASIO_NOEXCEPT
372   {
373     return a.impl_ == b.impl_;
374   }
375 
376   /// Compare two strands for inequality.
377   /**
378    * Two strands are equal if they refer to the same ordered, non-concurrent
379    * state.
380    */
operator !=(const strand & a,const strand & b)381   friend bool operator!=(const strand& a, const strand& b) BOOST_ASIO_NOEXCEPT
382   {
383     return a.impl_ != b.impl_;
384   }
385 
386 #if defined(GENERATING_DOCUMENTATION)
387 private:
388 #endif // defined(GENERATING_DOCUMENTATION)
389   typedef detail::strand_executor_service::implementation_type
390     implementation_type;
391 
392   template <typename InnerExecutor>
create_implementation(const InnerExecutor & ex,typename constraint<can_query<InnerExecutor,execution::context_t>::value>::type=0)393   static implementation_type create_implementation(const InnerExecutor& ex,
394       typename constraint<
395         can_query<InnerExecutor, execution::context_t>::value
396       >::type = 0)
397   {
398     return use_service<detail::strand_executor_service>(
399         boost::asio::query(ex, execution::context)).create_implementation();
400   }
401 
402   template <typename InnerExecutor>
create_implementation(const InnerExecutor & ex,typename constraint<!can_query<InnerExecutor,execution::context_t>::value>::type=0)403   static implementation_type create_implementation(const InnerExecutor& ex,
404       typename constraint<
405         !can_query<InnerExecutor, execution::context_t>::value
406       >::type = 0)
407   {
408     return use_service<detail::strand_executor_service>(
409         ex.context()).create_implementation();
410   }
411 
strand(const Executor & ex,const implementation_type & impl)412   strand(const Executor& ex, const implementation_type& impl)
413     : executor_(ex),
414       impl_(impl)
415   {
416   }
417 
418   template <typename Property>
query_helper(false_type,const Property & property) const419   typename query_result<const Executor&, Property>::type query_helper(
420       false_type, const Property& property) const
421   {
422     return boost::asio::query(executor_, property);
423   }
424 
425   template <typename Property>
query_helper(true_type,const Property & property) const426   execution::blocking_t query_helper(true_type, const Property& property) const
427   {
428     execution::blocking_t result = boost::asio::query(executor_, property);
429     return result == execution::blocking.always
430       ? execution::blocking.possibly : result;
431   }
432 
433   Executor executor_;
434   implementation_type impl_;
435 };
436 
437 /** @defgroup make_strand boost::asio::make_strand
438  *
439  * @brief The boost::asio::make_strand function creates a @ref strand object for
440  * an executor or execution context.
441  */
442 /*@{*/
443 
444 /// Create a @ref strand object for an executor.
445 template <typename Executor>
make_strand(const Executor & ex,typename constraint<is_executor<Executor>::value||execution::is_executor<Executor>::value>::type=0)446 inline strand<Executor> make_strand(const Executor& ex,
447     typename constraint<
448       is_executor<Executor>::value || execution::is_executor<Executor>::value
449     >::type = 0)
450 {
451   return strand<Executor>(ex);
452 }
453 
454 /// Create a @ref strand object for an execution context.
455 template <typename ExecutionContext>
456 inline strand<typename ExecutionContext::executor_type>
make_strand(ExecutionContext & ctx,typename constraint<is_convertible<ExecutionContext &,execution_context &>::value>::type=0)457 make_strand(ExecutionContext& ctx,
458     typename constraint<
459       is_convertible<ExecutionContext&, execution_context&>::value
460     >::type = 0)
461 {
462   return strand<typename ExecutionContext::executor_type>(ctx.get_executor());
463 }
464 
465 /*@}*/
466 
467 #if !defined(GENERATING_DOCUMENTATION)
468 
469 namespace traits {
470 
471 #if !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
472 
473 template <typename Executor>
474 struct equality_comparable<strand<Executor> >
475 {
476   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
477   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = true);
478 };
479 
480 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
481 
482 #if !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
483 
484 template <typename Executor, typename Function>
485 struct execute_member<strand<Executor>, Function,
486     typename enable_if<
487       execution::can_execute<const Executor&, Function>::value
488     >::type>
489 {
490   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
491   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept = false);
492   typedef void result_type;
493 };
494 
495 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
496 
497 #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
498 
499 template <typename Executor, typename Property>
500 struct query_member<strand<Executor>, Property,
501     typename enable_if<
502       can_query<const Executor&, Property>::value
503     >::type>
504 {
505   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
506   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept =
507       (is_nothrow_query<Executor, Property>::value));
508   typedef typename conditional<
509     is_convertible<Property, execution::blocking_t>::value,
510       execution::blocking_t, typename query_result<Executor, Property>::type
511         >::type result_type;
512 };
513 
514 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
515 
516 #if !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
517 
518 template <typename Executor, typename Property>
519 struct require_member<strand<Executor>, Property,
520     typename enable_if<
521       can_require<const Executor&, Property>::value
522         && !is_convertible<Property, execution::blocking_t::always_t>::value
523     >::type>
524 {
525   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
526   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept =
527       (is_nothrow_require<Executor, Property>::value));
528   typedef strand<typename decay<
529     typename require_result<Executor, Property>::type
530       >::type> result_type;
531 };
532 
533 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
534 
535 #if !defined(BOOST_ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
536 
537 template <typename Executor, typename Property>
538 struct prefer_member<strand<Executor>, Property,
539     typename enable_if<
540       can_prefer<const Executor&, Property>::value
541         && !is_convertible<Property, execution::blocking_t::always_t>::value
542     >::type>
543 {
544   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_valid = true);
545   BOOST_ASIO_STATIC_CONSTEXPR(bool, is_noexcept =
546       (is_nothrow_prefer<Executor, Property>::value));
547   typedef strand<typename decay<
548     typename prefer_result<Executor, Property>::type
549       >::type> result_type;
550 };
551 
552 #endif // !defined(BOOST_ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
553 
554 } // namespace traits
555 
556 #endif // !defined(GENERATING_DOCUMENTATION)
557 
558 } // namespace asio
559 } // namespace boost
560 
561 #include <boost/asio/detail/pop_options.hpp>
562 
563 // If both io_context.hpp and strand.hpp have been included, automatically
564 // include the header file needed for the io_context::strand class.
565 #if !defined(BOOST_ASIO_NO_EXTENSIONS)
566 # if defined(BOOST_ASIO_IO_CONTEXT_HPP)
567 #  include <boost/asio/io_context_strand.hpp>
568 # endif // defined(BOOST_ASIO_IO_CONTEXT_HPP)
569 #endif // !defined(BOOST_ASIO_NO_EXTENSIONS)
570 
571 #endif // BOOST_ASIO_STRAND_HPP
572