added asio

This commit is contained in:
2026-02-22 20:48:24 +01:00
parent 238108b69a
commit b42921d2da
633 changed files with 173388 additions and 0 deletions

View File

@@ -0,0 +1,817 @@
//
// 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 ASIO_INLINE_OR_EXECUTOR_HPP
#define ASIO_INLINE_OR_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/non_const_lvalue.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/execution/blocking.hpp"
#include "asio/execution/executor.hpp"
#include "asio/execution/inline_exception_handling.hpp"
#include "asio/execution_context.hpp"
#include "asio/is_executor.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
/// Adapts an executor to add inline invocation of the submitted function.
/**
* The @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 <typename Executor,
typename Blocking = execution::blocking_t::always_t,
typename InlineExceptionHandling
= execution::inline_exception_handling_t::propagate_t>
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 <typename Executor1>
explicit inline_or_executor(const Executor1& e,
constraint_t<
conditional_t<
!is_same<Executor1, inline_or_executor>::value,
is_convertible<Executor1, Executor>,
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 <class OtherExecutor>
inline_or_executor(
const inline_or_executor<OtherExecutor>& 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 <class OtherExecutor>
inline_or_executor& operator=(
const inline_or_executor<OtherExecutor>& other) noexcept
{
executor_ = other.executor_;
return *this;
}
/// Move constructor.
inline_or_executor(inline_or_executor&& other) noexcept
: executor_(static_cast<Executor&&>(other.executor_))
{
}
/// Converting move constructor.
/**
* This constructor is only valid if the @c OtherExecutor type is convertible
* to @c Executor.
*/
template <class OtherExecutor>
inline_or_executor(inline_or_executor<OtherExecutor>&& other) noexcept
: executor_(static_cast<OtherExecutor&&>(other.executor_))
{
}
/// Move assignment operator.
inline_or_executor& operator=(inline_or_executor&& other) noexcept
{
executor_ = static_cast<Executor&&>(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 <class OtherExecutor>
inline_or_executor& operator=(
inline_or_executor<OtherExecutor>&& other) noexcept
{
executor_ = static_cast<OtherExecutor&&>(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
* asio::query customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* if (asio::query(ex, asio::execution::blocking)
* == 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
* asio::query customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* if (asio::query(ex,
* asio::execution::inline_exception_handling)
* == 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
* asio::query customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* if (asio::query(ex, asio::execution::blocking)
* == asio::execution::blocking.never)
* ... @endcode
*/
template <typename Property>
query_result_t<const Executor&, Property> query(const Property& p,
constraint_t<
can_query<const Executor&, Property>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::blocking_t>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::inline_exception_handling_t>::value
> = 0) const
noexcept(is_nothrow_query<const Executor&, Property>::value)
{
return 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
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::blocking.possibly); @endcode
*/
inline_or_executor<Executor, execution::blocking_t::possibly_t,
InlineExceptionHandling>
require(const execution::blocking_t::possibly_t&) const noexcept
{
return inline_or_executor<Executor, execution::blocking_t::possibly_t,
InlineExceptionHandling>(executor_);
}
/// Obtain an executor with the @c blocking.always property.
/**
* Do not call this function directly. It is intended for use with the
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::blocking.always); @endcode
*/
inline_or_executor<Executor, execution::blocking_t::always_t,
InlineExceptionHandling>
require(const execution::blocking_t::always_t&) const noexcept
{
return inline_or_executor<Executor, execution::blocking_t::always_t,
InlineExceptionHandling>(executor_);
}
/// Obtain an executor with the @c blocking.never property.
/**
* Do not call this function directly. It is intended for use with the
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::blocking.never); @endcode
*/
inline_or_executor<Executor, execution::blocking_t::never_t,
InlineExceptionHandling>
require(const execution::blocking_t::never_t&) const noexcept
{
return inline_or_executor<Executor, execution::blocking_t::never_t,
InlineExceptionHandling>(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
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::inline_exception_handling.propagate); @endcode
*/
inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::propagate_t>
require(const execution::inline_exception_handling_t::propagate_t&)
const noexcept
{
return inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::propagate_t>(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
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::inline_exception_handling.terminate); @endcode
*/
inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::terminate_t>
require(const execution::inline_exception_handling_t::terminate_t&)
const noexcept
{
return inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::terminate_t>(executor_);
}
/// Forward a requirement to the underlying executor.
/**
* Do not call this function directly. It is intended for use with the
* asio::require customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex1 = ...;
* auto ex2 = asio::require(ex1,
* asio::execution::relationship.continuation); @endcode
*/
template <typename Property>
inline_or_executor<decay_t<require_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling>
require(const Property& p,
constraint_t<
can_require<const Executor&, Property>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::blocking_t>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::inline_exception_handling_t>::value
> = 0) const
noexcept(is_nothrow_require<const Executor&, Property>::value)
{
return inline_or_executor<
decay_t<require_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling>(asio::require(executor_, p));
}
/// Forward a preference to the underlying executor.
/**
* Do not call this function directly. It is intended for use with the
* asio::prefer customisation point.
*
* For example:
* @code asio::inline_or_executor<my_executor_type> ex1 = ...;
* auto ex2 = asio::prefer(ex1,
* asio::execution::relationship.continuation); @endcode
*/
template <typename Property>
inline_or_executor<decay_t<prefer_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling>
prefer(const Property& p,
constraint_t<
can_prefer<const Executor&, Property>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::blocking_t>::value
> = 0,
constraint_t<
!is_convertible<Property, execution::inline_exception_handling_t>::value
> = 0) const
noexcept(is_nothrow_prefer<const Executor&, Property>::value)
{
return inline_or_executor<
decay_t<prefer_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling>(asio::prefer(executor_, p));
}
#if !defined(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(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 <typename Function>
constraint_t<
traits::execute_member<const Executor&, Function>::is_valid,
void
> execute(Function&& f) const
{
this->execute_helper(static_cast<Function&&>(f), Blocking{});
}
#if !defined(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 <typename Function, typename Allocator>
void dispatch(Function&& f, const Allocator& a) const
{
(void)a;
detail::non_const_lvalue<Function> f2(f);
static_cast<decay_t<Function>&&>(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 <typename Function, typename Allocator>
void post(Function&& f, const Allocator& a) const
{
executor_.post(static_cast<Function&&>(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 <typename Function, typename Allocator>
void defer(Function&& f, const Allocator& a) const
{
executor_.defer(static_cast<Function&&>(f), a);
}
#endif // !defined(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 <typename Function>
void execute_helper(Function&& f, execution::blocking_t::possibly_t) const
{
#if !defined(ASIO_NO_EXCEPTIONS)
try
#endif // !defined(ASIO_NO_EXCEPTIONS)
{
detail::non_const_lvalue<Function> f2(f);
static_cast<decay_t<Function>&&>(f2.value)();
}
#if !defined(ASIO_NO_EXCEPTIONS)
catch (...)
{
if (is_same<InlineExceptionHandling,
execution::inline_exception_handling_t::terminate_t>::value)
{
std::terminate();
}
else
{
throw;
}
}
#endif // !defined(ASIO_NO_EXCEPTIONS)
}
template <typename Function>
void execute_helper(Function&& f, execution::blocking_t::always_t) const
{
this->execute_helper(static_cast<Function&&>(f),
execution::blocking.possibly);
}
template <typename Function>
void execute_helper(Function&& f, execution::blocking_t::never_t) const
{
asio::require(executor_, execution::blocking.never).execute(
static_cast<Function&&>(f));
}
Executor executor_;
};
/** @defgroup inline_or asio::inline_or
*
* @brief The 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 <typename Executor>
inline inline_or_executor<Executor> inline_or(const Executor& ex,
constraint_t<
is_executor<Executor>::value || execution::is_executor<Executor>::value
> = 0)
{
return inline_or_executor<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 <tt>ctx.get_executor()</tt>.
*/
template <typename ExecutionContext>
inline inline_or_executor<typename ExecutionContext::executor_type>
inline_or(ExecutionContext& ctx,
constraint_t<
is_convertible<ExecutionContext&, execution_context&>::value
> = 0)
{
return inline_or_executor<typename ExecutionContext::executor_type>(
ctx.get_executor());
}
/*@}*/
#if !defined(GENERATING_DOCUMENTATION)
namespace traits {
#if !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct equality_comparable<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
};
#endif // !defined(ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Function>
struct execute_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>, Function,
enable_if_t<
traits::execute_member<const Executor&, Function>::is_valid
>
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = false;
typedef void result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Property>
struct query_static_constexpr_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
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 <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Property>
struct query_static_constexpr_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
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(ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Property>
struct query_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>, Property,
enable_if_t<
can_query<const Executor&, Property>::value
&& !is_convertible<Property,
execution::blocking_t>::value
&& !is_convertible<Property,
execution::inline_exception_handling_t>::value
>
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept =
is_nothrow_query<Executor, Property>::value;
typedef query_result_t<Executor, Property> result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
execution::blocking_t::possibly_t
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
typedef inline_or_executor<Executor, execution::blocking_t::possibly_t,
InlineExceptionHandling> result_type;
};
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
execution::blocking_t::always_t
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
typedef inline_or_executor<Executor, execution::blocking_t::always_t,
InlineExceptionHandling> result_type;
};
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
execution::blocking_t::never_t
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
typedef inline_or_executor<Executor, execution::blocking_t::never_t,
InlineExceptionHandling> result_type;
};
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
execution::inline_exception_handling_t::propagate_t
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
typedef inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::propagate_t> result_type;
};
template <typename Executor, typename Blocking,
typename InlineExceptionHandling>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>,
execution::inline_exception_handling_t::terminate_t
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept = true;
typedef inline_or_executor<Executor, Blocking,
execution::inline_exception_handling_t::terminate_t> result_type;
};
template <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Property>
struct require_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>, Property,
enable_if_t<
can_require<const Executor&, Property>::value
&& !is_convertible<Property,
execution::blocking_t>::value
&& !is_convertible<Property,
execution::inline_exception_handling_t>::value
>
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept =
is_nothrow_require<const Executor&, Property>::value;
typedef inline_or_executor<
decay_t<require_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling> result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
#if !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
template <typename Executor, typename Blocking,
typename InlineExceptionHandling, typename Property>
struct prefer_member<
inline_or_executor<Executor, Blocking, InlineExceptionHandling>, Property,
enable_if_t<
can_prefer<const Executor&, Property>::value
&& !is_convertible<Property, execution::blocking_t::always_t>::value
>
>
{
static constexpr bool is_valid = true;
static constexpr bool is_noexcept =
is_nothrow_prefer<const Executor&, Property>::value;
typedef inline_or_executor<
decay_t<prefer_result_t<const Executor&, Property>>,
Blocking, InlineExceptionHandling> result_type;
};
#endif // !defined(ASIO_HAS_DEDUCED_PREFER_MEMBER_TRAIT)
} // namespace traits
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_INLINE_OR_EXECUTOR_HPP