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