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