// // inline_or_executor.hpp // ~~~~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BOOST_ASIO_INLINE_OR_EXECUTOR_HPP #define BOOST_ASIO_INLINE_OR_EXECUTOR_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include #include #include #include #include #include namespace boost { namespace asio { /// Adapts an executor to add inline invocation of the submitted function. /** * The @c inline_or_executor class template adapts an existing executor such * that: * * @li posted function objects (or when the @c blocking property is set to * @c blocking.never) are submitted to the wrapped executor; and * * @li dispatched function objects (or when @c blocking is @c blocking.always or * @c blocking.possibly) are executed inline. */ template class inline_or_executor { public: /// The type of the underlying executor. typedef Executor inner_executor_type; /// Default constructor. /** * This constructor is only valid if the underlying executor type is default * constructible. */ inline_or_executor() : executor_() { } /// Construct an inline_or_executor for the specified executor. template explicit inline_or_executor(const Executor1& e, constraint_t< conditional_t< !is_same::value, is_convertible, false_type >::value > = 0) : executor_(e) { } /// Copy constructor. inline_or_executor(const inline_or_executor& other) noexcept : executor_(other.executor_) { } /// Converting constructor. /** * This constructor is only valid if the @c OtherExecutor type is convertible * to @c Executor. */ template inline_or_executor( const inline_or_executor& other) noexcept : executor_(other.executor_) { } /// Assignment operator. inline_or_executor& operator=(const inline_or_executor& other) noexcept { executor_ = other.executor_; return *this; } /// Converting assignment operator. /** * This assignment operator is only valid if the @c OtherExecutor type is * convertible to @c Executor. */ template inline_or_executor& operator=( const inline_or_executor& other) noexcept { executor_ = other.executor_; return *this; } /// Move constructor. inline_or_executor(inline_or_executor&& other) noexcept : executor_(static_cast(other.executor_)) { } /// Converting move constructor. /** * This constructor is only valid if the @c OtherExecutor type is convertible * to @c Executor. */ template inline_or_executor(inline_or_executor&& other) noexcept : executor_(static_cast(other.executor_)) { } /// Move assignment operator. inline_or_executor& operator=(inline_or_executor&& other) noexcept { executor_ = static_cast(other.executor_); return *this; } /// Converting move assignment operator. /** * This assignment operator is only valid if the @c OtherExecutor type is * convertible to @c Executor. */ template inline_or_executor& operator=( inline_or_executor&& other) noexcept { executor_ = static_cast(other.executor_); return *this; } /// Destructor. ~inline_or_executor() noexcept { } /// Obtain the underlying executor. inner_executor_type get_inner_executor() const noexcept { return executor_; } /// Query the current value of the @c blocking property. /** * Do not call this function directly. It is intended for use with the * boost::asio::query customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * if (boost::asio::query(ex, boost::asio::execution::blocking) * == boost::asio::execution::blocking.possibly) * ... @endcode */ static constexpr execution::blocking_t query(execution::blocking_t) noexcept { return Blocking(); } /// Query the current value of the @c inline_exception_handling property. /** * Do not call this function directly. It is intended for use with the * boost::asio::query customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * if (boost::asio::query(ex, * boost::asio::execution::inline_exception_handling) * == boost::asio::execution::inline_exception_handling.propagate) * ... @endcode */ static constexpr execution::inline_exception_handling_t query( execution::inline_exception_handling_t) noexcept { return InlineExceptionHandling(); } /// Forward a query to the underlying executor. /** * Do not call this function directly. It is intended for use with the * boost::asio::query customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * if (boost::asio::query(ex, boost::asio::execution::blocking) * == boost::asio::execution::blocking.never) * ... @endcode */ template query_result_t query(const Property& p, constraint_t< can_query::value > = 0, constraint_t< !is_convertible::value > = 0, constraint_t< !is_convertible::value > = 0) const noexcept(is_nothrow_query::value) { return boost::asio::query(executor_, p); } /// Obtain an executor with the @c blocking.possibly property. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::blocking.possibly); @endcode */ inline_or_executor require(const execution::blocking_t::possibly_t&) const noexcept { return inline_or_executor(executor_); } /// Obtain an executor with the @c blocking.always property. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::blocking.always); @endcode */ inline_or_executor require(const execution::blocking_t::always_t&) const noexcept { return inline_or_executor(executor_); } /// Obtain an executor with the @c blocking.never property. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::blocking.never); @endcode */ inline_or_executor require(const execution::blocking_t::never_t&) const noexcept { return inline_or_executor(executor_); } /// Obtain an executor with the @c inline_exception_handling.propagate /// property. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::inline_exception_handling.propagate); @endcode */ inline_or_executor require(const execution::inline_exception_handling_t::propagate_t&) const noexcept { return inline_or_executor(executor_); } /// Obtain an executor with the @c inline_exception_handling.terminate /// property. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::inline_exception_handling.terminate); @endcode */ inline_or_executor require(const execution::inline_exception_handling_t::terminate_t&) const noexcept { return inline_or_executor(executor_); } /// Forward a requirement to the underlying executor. /** * Do not call this function directly. It is intended for use with the * boost::asio::require customisation point. * * For example: * @code boost::asio::inline_or_executor ex1 = ...; * auto ex2 = boost::asio::require(ex1, * boost::asio::execution::relationship.continuation); @endcode */ template inline_or_executor>, Blocking, InlineExceptionHandling> require(const Property& p, constraint_t< can_require::value > = 0, constraint_t< !is_convertible::value > = 0, constraint_t< !is_convertible::value > = 0) const noexcept(is_nothrow_require::value) { return inline_or_executor< decay_t>, Blocking, InlineExceptionHandling>(boost::asio::require(executor_, p)); } /// Forward a preference to the underlying executor. /** * Do not call this function directly. It is intended for use with the * boost::asio::prefer customisation point. * * For example: * @code boost::asio::inline_or_executor ex1 = ...; * auto ex2 = boost::asio::prefer(ex1, * boost::asio::execution::relationship.continuation); @endcode */ template inline_or_executor>, Blocking, InlineExceptionHandling> prefer(const Property& p, constraint_t< can_prefer::value > = 0, constraint_t< !is_convertible::value > = 0, constraint_t< !is_convertible::value > = 0) const noexcept(is_nothrow_prefer::value) { return inline_or_executor< decay_t>, Blocking, InlineExceptionHandling>(boost::asio::prefer(executor_, p)); } #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) /// Obtain the underlying execution context. execution_context& context() const noexcept { return executor_.context(); } /// Inform the inline_or_executor that it has some outstanding work to do. /** * The inline_or_executor delegates this call to its underlying executor. */ void on_work_started() const noexcept { executor_.on_work_started(); } /// Inform the inline_or_executor that some work is no longer outstanding. /** * The inline_or_executor delegates this call to its underlying executor. */ void on_work_finished() const noexcept { executor_.on_work_finished(); } #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) /// Request the inline_or_executor to invoke the given function object. /** * This function is used to ask the inline_or_executor to execute the given * function object. The function object will be executed inline or according * to the properties of the underlying executor. * * @param f The function object to be called. The executor will make * a copy of the handler object as required. The function signature of the * function object must be: @code void function(); @endcode */ template constraint_t< traits::execute_member::is_valid, void > execute(Function&& f) const { this->execute_helper(static_cast(f), Blocking{}); } #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) /// Request the inline_or_executor to invoke the given function object. /** * This function is used to ask the inline_or_executor to execute the given * function object. The function object will be executed inside this function. * * @param f The function object to be called. The executor will make * a copy of the handler object as required. The function signature of the * function object must be: @code void function(); @endcode * * @param a An allocator that may be used by the executor to allocate the * internal storage needed for function invocation. */ template void dispatch(Function&& f, const Allocator& a) const { (void)a; detail::non_const_lvalue f2(f); static_cast&&>(f2.value)(); } /// Request the inline_or_executor to invoke the given function object. /** * This function is used to ask the executor to execute the given function * object. The function object will never be executed inside this function. * Instead, it will be scheduled by the underlying executor's post function. * * @param f The function object to be called. The executor will make * a copy of the handler object as required. The function signature of the * function object must be: @code void function(); @endcode * * @param a An allocator that may be used by the executor to allocate the * internal storage needed for function invocation. */ template void post(Function&& f, const Allocator& a) const { executor_.post(static_cast(f), a); } /// Request the inline_or_executor to invoke the given function object. /** * This function is used to ask the executor to execute the given function * object. The function object will never be executed inside this function. * Instead, it will be scheduled by the underlying executor's defer function. * * @param f The function object to be called. The executor will make * a copy of the handler object as required. The function signature of the * function object must be: @code void function(); @endcode * * @param a An allocator that may be used by the executor to allocate the * internal storage needed for function invocation. */ template void defer(Function&& f, const Allocator& a) const { executor_.defer(static_cast(f), a); } #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) /// Compare two inline_or_executors for equality. /** * Two inline_or_executors are equal if their underlying executors are equal. */ friend bool operator==(const inline_or_executor& a, const inline_or_executor& b) noexcept { return a.executor_ == b.executor_; } /// Compare two inline_or_executors for inequality. /** * Two inline_or_executors are equal if their underlying executors are equal. */ friend bool operator!=(const inline_or_executor& a, const inline_or_executor& b) noexcept { return a.executor_ != b.executor_; } #if defined(GENERATING_DOCUMENTATION) private: #endif // defined(GENERATING_DOCUMENTATION) template void execute_helper(Function&& f, execution::blocking_t::possibly_t) const { #if !defined(BOOST_ASIO_NO_EXCEPTIONS) try #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) { detail::non_const_lvalue f2(f); static_cast&&>(f2.value)(); } #if !defined(BOOST_ASIO_NO_EXCEPTIONS) catch (...) { if (is_same::value) { std::terminate(); } else { throw; } } #endif // !defined(BOOST_ASIO_NO_EXCEPTIONS) } template void execute_helper(Function&& f, execution::blocking_t::always_t) const { this->execute_helper(static_cast(f), execution::blocking.possibly); } template void execute_helper(Function&& f, execution::blocking_t::never_t) const { boost::asio::require(executor_, execution::blocking.never).execute( static_cast(f)); } Executor executor_; }; /** @defgroup inline_or boost::asio::inline_or * * @brief The boost::asio::inline_or function creates an @ref inline_or_executor * object for an executor or execution context. */ /*@{*/ /// Create an @ref inline_or_executor object for an executor. /** * @param ex An executor. * * @returns An inline_or_executor constructed with the specified executor. */ template inline inline_or_executor inline_or(const Executor& ex, constraint_t< is_executor::value || execution::is_executor::value > = 0) { return inline_or_executor(ex); } /// Create an @ref inline_or_executor object for an execution context. /** * @param ctx An execution context, from which an executor will be obtained. * * @returns An inline_or_executor constructed with the execution context's * executor, obtained by performing ctx.get_executor(). */ template inline inline_or_executor inline_or(ExecutionContext& ctx, constraint_t< is_convertible::value > = 0) { return inline_or_executor( ctx.get_executor()); } /*@}*/ #if !defined(GENERATING_DOCUMENTATION) namespace traits { #if !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) template struct equality_comparable< inline_or_executor> { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT) #if !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) template struct execute_member< inline_or_executor, Function, enable_if_t< traits::execute_member::is_valid > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = false; typedef void result_type; }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT) #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) template struct query_static_constexpr_member< inline_or_executor, Property, enable_if_t< is_convertible< Property, execution::blocking_t >::value > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef Blocking result_type; static constexpr result_type value() noexcept { return result_type(); } }; template struct query_static_constexpr_member< inline_or_executor, Property, enable_if_t< is_convertible< Property, execution::inline_exception_handling_t >::value > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef InlineExceptionHandling result_type; static constexpr result_type value() noexcept { return result_type(); } }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT) #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) template struct query_member< inline_or_executor, Property, enable_if_t< can_query::value && !is_convertible::value && !is_convertible::value > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = is_nothrow_query::value; typedef query_result_t result_type; }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT) #if !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) template struct require_member< inline_or_executor, execution::blocking_t::possibly_t > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef inline_or_executor result_type; }; template struct require_member< inline_or_executor, execution::blocking_t::always_t > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef inline_or_executor result_type; }; template struct require_member< inline_or_executor, execution::blocking_t::never_t > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef inline_or_executor result_type; }; template struct require_member< inline_or_executor, execution::inline_exception_handling_t::propagate_t > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef inline_or_executor result_type; }; template struct require_member< inline_or_executor, execution::inline_exception_handling_t::terminate_t > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = true; typedef inline_or_executor result_type; }; template struct require_member< inline_or_executor, Property, enable_if_t< can_require::value && !is_convertible::value && !is_convertible::value > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = is_nothrow_require::value; typedef inline_or_executor< decay_t>, Blocking, InlineExceptionHandling> result_type; }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT) #if !defined(BOOST_ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) template struct prefer_member< inline_or_executor, Property, enable_if_t< can_prefer::value && !is_convertible::value > > { static constexpr bool is_valid = true; static constexpr bool is_noexcept = is_nothrow_prefer::value; typedef inline_or_executor< decay_t>, Blocking, InlineExceptionHandling> result_type; }; #endif // !defined(BOOST_ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT) } // namespace traits #endif // !defined(GENERATING_DOCUMENTATION) } // namespace asio } // namespace boost #include #endif // BOOST_ASIO_INLINE_OR_EXECUTOR_HPP