Skip to content

Commit

Permalink
feat: Asio and execution may now be used within the same applicatio…
Browse files Browse the repository at this point in the history
…n. Simply link with `asio-grpc::asio-grpc(-standalone-asio)` and `asio-grpc::asio-grpc-(unifex|stdexec)`
  • Loading branch information
Tradias committed Dec 11, 2023
1 parent fe4b00c commit b45f41b
Show file tree
Hide file tree
Showing 39 changed files with 363 additions and 287 deletions.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ if(ASIO_GRPC_BUILD_TESTS AND ASIO_GRPC_ENABLE_CHECK_HEADER_SYNTAX_TARGET)
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/allocate_operation.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/allocation_type.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/asio_forward.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/asio_utils.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/associated_completion_handler.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/association.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/association_asio.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/association_unifex.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/async_initiate.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/atomic.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/agrpc/detail/atomic_intrusive_queue.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/agrpc/bind_allocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class AllocatorBinder
/**
* @brief Get the target's associated executor
*/
constexpr decltype(auto) get_executor() const noexcept { return detail::exec::get_executor(get()); }
constexpr decltype(auto) get_executor() const noexcept { return detail::get_executor(get()); }

/**
* @brief Get the bound allocator
Expand Down
6 changes: 3 additions & 3 deletions src/agrpc/detail/allocate_operation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#include <agrpc/detail/allocate.hpp>
#include <agrpc/detail/allocation_type.hpp>
#include <agrpc/detail/association.hpp>
#include <agrpc/detail/config.hpp>
#include <agrpc/detail/execution.hpp>
#include <agrpc/detail/grpc_context_implementation.hpp>
#include <agrpc/detail/operation.hpp>
#include <agrpc/detail/utility.hpp>
Expand All @@ -37,7 +37,7 @@ inline constexpr bool IS_STD_ALLOCATOR<std::allocator<T>> = true;
template <template <class> class OperationTemplate, class Handler, class... Args>
auto allocate_custom_operation(Handler&& handler, Args&&... args)
{
const auto allocator = exec::get_allocator(handler);
const auto allocator = detail::get_allocator(handler);
auto operation = detail::allocate<OperationTemplate<detail::RemoveCrefT<Handler>>>(
allocator, detail::AllocationType::CUSTOM, static_cast<Handler&&>(handler), static_cast<Args&&>(args)...);
return operation.release();
Expand All @@ -47,7 +47,7 @@ template <template <class> class OperationTemplate, class Handler, class... Args
auto allocate_local_operation(agrpc::GrpcContext& grpc_context, Handler&& handler, Args&&... args)
{
using DecayedHandler = detail::RemoveCrefT<Handler>;
auto allocator = exec::get_allocator(handler);
auto allocator = detail::get_allocator(handler);
if constexpr (detail::IS_STD_ALLOCATOR<decltype(allocator)>)
{
auto operation = detail::allocate<OperationTemplate<DecayedHandler>>(
Expand Down
6 changes: 3 additions & 3 deletions src/agrpc/detail/asio_forward.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ using ErrorCode = boost::system::error_code;
using ErrorCode = std::error_code;
#endif

#if defined(AGRPC_UNIFEX) || defined(AGRPC_STDEXEC)
inline auto operation_aborted_error_code() { return ErrorCode{}; }
#else
#if defined(AGRPC_BOOST_ASIO) || defined(AGRPC_STANDALONE_ASIO)
inline auto operation_aborted_error_code() { return ErrorCode{asio::error::operation_aborted}; }
#else
inline auto operation_aborted_error_code() { return ErrorCode{}; }
#endif
} // namespace detail

Expand Down
117 changes: 117 additions & 0 deletions src/agrpc/detail/asio_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2023 Dennis Hezel
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AGRPC_DETAIL_ASIO_UTILS_HPP
#define AGRPC_DETAIL_ASIO_UTILS_HPP

#include <agrpc/detail/config.hpp>
#include <agrpc/detail/execution.hpp>

#if defined(AGRPC_STANDALONE_ASIO) || defined(AGRPC_BOOST_ASIO)

AGRPC_NAMESPACE_BEGIN()

namespace detail
{
template <class, class = void>
inline constexpr bool IS_EXECUTOR_PROVIDER = false;

template <class T>
inline constexpr bool IS_EXECUTOR_PROVIDER<T, decltype((void)std::declval<T>().get_executor())> = true;

#ifdef AGRPC_ASIO_HAS_SENDER_RECEIVER
template <class Executor, class Function>
void execute(Executor&& executor, Function&& function)
{
asio::execution::execute(static_cast<Executor&&>(executor), static_cast<Function&&>(function));
}
#else
template <class Executor, class Function>
void execute(Executor&& executor, Function&& function)
{
static_cast<Executor&&>(executor).execute(static_cast<Function&&>(function));
}
#endif

template <class Executor, class Function, class Allocator>
void post_with_allocator(Executor&& executor, Function&& function, const Allocator& allocator)
{
detail::execute(asio::prefer(asio::require(static_cast<Executor&&>(executor), asio::execution::blocking_t::never),
asio::execution::relationship_t::fork, asio::execution::allocator(allocator)),
static_cast<Function&&>(function));
}

template <class CompletionHandler, class Function, class IOExecutor>
void complete_immediately(CompletionHandler&& completion_handler, Function&& function, const IOExecutor& io_executor)
{
const auto allocator = asio::get_associated_allocator(completion_handler);
#ifdef AGRPC_ASIO_HAS_IMMEDIATE_EXECUTOR
auto executor = asio::get_associated_immediate_executor(
completion_handler,
[&]() -> decltype(auto)
{
// Ensure that the io_executor is not already blocking::never because asio will try to convert const& to &&
// due to chosing the `identity` overload of asio::require within the default_immediate_executor.
// https://github.com/chriskohlhoff/asio/issues/1392
if constexpr (asio::traits::static_require<IOExecutor, asio::execution::blocking_t::never_t>::is_valid)
{
return asio::prefer(io_executor, asio::execution::blocking_t::possibly);
}
else
{
return (io_executor);
}
}());
detail::execute(
asio::prefer(std::move(executor), asio::execution::allocator(allocator)),
[ch = static_cast<CompletionHandler&&>(completion_handler), f = static_cast<Function&&>(function)]() mutable
{
static_cast<Function&&>(f)(static_cast<CompletionHandler&&>(ch));
});
#else
auto executor = asio::get_associated_executor(completion_handler, io_executor);
detail::post_with_allocator(
std::move(executor),
[ch = static_cast<CompletionHandler&&>(completion_handler), f = static_cast<Function&&>(function)]() mutable
{
static_cast<Function&&>(f)(static_cast<CompletionHandler&&>(ch));
},
allocator);
#endif
}

struct UncancellableToken
{
static constexpr bool is_connected() noexcept { return false; }
};

template <class Object>
auto get_cancellation_slot([[maybe_unused]] const Object& object) noexcept
{
#ifdef AGRPC_ASIO_HAS_CANCELLATION_SLOT
return asio::get_associated_cancellation_slot(object, UncancellableToken{});
#else
return UncancellableToken{};
#endif
}

template <class T>
using CancellationSlotT = decltype(detail::get_cancellation_slot(std::declval<T>()));
} // namespace detail

AGRPC_NAMESPACE_END

#endif

#endif // AGRPC_DETAIL_ASIO_UTILS_HPP
3 changes: 1 addition & 2 deletions src/agrpc/detail/association.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
#define AGRPC_DETAIL_ASSOCIATION_HPP

#include <agrpc/detail/config.hpp>
#include <agrpc/detail/execution.hpp>

#if defined(AGRPC_STANDALONE_ASIO) || defined(AGRPC_BOOST_ASIO)
#include <agrpc/detail/association_asio.hpp>
#else
#include <agrpc/detail/association_unifex.hpp>
#include <agrpc/detail/association_execution.hpp>
#endif

#endif // AGRPC_DETAIL_ASSOCIATION_HPP
109 changes: 32 additions & 77 deletions src/agrpc/detail/association_asio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,103 +12,58 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AGRPC_DETAIL_ASIO_ASSOCIATION_HPP
#define AGRPC_DETAIL_ASIO_ASSOCIATION_HPP
#ifndef AGRPC_DETAIL_ASSOCIATION_ASIO_HPP
#define AGRPC_DETAIL_ASSOCIATION_ASIO_HPP

#include <agrpc/detail/asio_utils.hpp>
#include <agrpc/detail/config.hpp>
#include <agrpc/detail/execution.hpp>

AGRPC_NAMESPACE_BEGIN()

namespace detail
{
#ifdef AGRPC_ASIO_HAS_SENDER_RECEIVER
template <class Executor, class Function>
void do_execute(Executor&& executor, Function&& function)
{
asio::execution::execute(static_cast<Executor&&>(executor), static_cast<Function&&>(function));
}
#else
template <class Executor, class Function>
void do_execute(Executor&& executor, Function&& function)
{
static_cast<Executor&&>(executor).execute(static_cast<Function&&>(function));
}
#endif

template <class Executor, class Function, class Allocator>
void post_with_allocator(Executor&& executor, Function&& function, const Allocator& allocator)
{
detail::do_execute(
asio::prefer(asio::require(static_cast<Executor&&>(executor), asio::execution::blocking_t::never),
asio::execution::relationship_t::fork, asio::execution::allocator(allocator)),
static_cast<Function&&>(function));
}

template <class CompletionHandler, class Function, class IOExecutor>
void complete_immediately(CompletionHandler&& completion_handler, Function&& function, const IOExecutor& io_executor)
{
const auto allocator = asio::get_associated_allocator(completion_handler);
#ifdef AGRPC_ASIO_HAS_IMMEDIATE_EXECUTOR
auto executor = asio::get_associated_immediate_executor(
completion_handler,
[&]() -> decltype(auto)
{
// Ensure that the io_executor is not already blocking::never because asio will try to convert const& to &&
// due to chosing the `identity` overload of asio::require within the default_immediate_executor.
// https://github.com/chriskohlhoff/asio/issues/1392
if constexpr (asio::traits::static_require<IOExecutor, asio::execution::blocking_t::never_t>::is_valid)
{
return asio::prefer(io_executor, asio::execution::blocking_t::possibly);
}
else
{
return (io_executor);
}
}());
detail::do_execute(
asio::prefer(std::move(executor), asio::execution::allocator(allocator)),
[ch = static_cast<CompletionHandler&&>(completion_handler), f = static_cast<Function&&>(function)]() mutable
{
static_cast<Function&&>(f)(static_cast<CompletionHandler&&>(ch));
});
#else
auto executor = asio::get_associated_executor(completion_handler, io_executor);
detail::post_with_allocator(
std::move(executor),
[ch = static_cast<CompletionHandler&&>(completion_handler), f = static_cast<Function&&>(function)]() mutable
{
static_cast<Function&&>(f)(static_cast<CompletionHandler&&>(ch));
},
allocator);
#endif
}
template <class CancellationSlot, class = void>
inline constexpr bool IS_CANCELLATION_SLOT = true;

template <class CancellationSlot>
bool stop_possible(CancellationSlot& cancellation_slot)
{
return cancellation_slot.is_connected();
}
inline constexpr bool
IS_CANCELLATION_SLOT<CancellationSlot, decltype((void)std::declval<CancellationSlot>().stop_requested())> = false;

template <class T, class = std::false_type>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V = IS_CANCELLATION_SLOT<T>;

template <class T>
constexpr bool stop_requested(const T&) noexcept
{
return false;
}
using IsStopEverPossibleHelper = std::bool_constant<(T{}.stop_possible())>;

template <class CancellationSlot>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V = true;
template <class T>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V<T, detail::IsStopEverPossibleHelper<T>> = false;

template <>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V<detail::UnstoppableCancellationSlot> = false;
inline constexpr bool IS_STOP_EVER_POSSIBLE_V<UncancellableToken> = false;

template <class T, class A = std::allocator<void>>
using AssociatedAllocatorT = asio::associated_allocator_t<T, A>;
template <class T>
inline constexpr bool IS_EXECUTOR = asio::is_executor<T>::value || asio::execution::is_executor_v<T>;

template <class T, class E = asio::system_executor>
using AssociatedExecutorT = asio::associated_executor_t<T, E>;

template <class T, class A = std::allocator<void>>
using AssociatedAllocatorT = asio::associated_allocator_t<T, A>;

template <class Object>
decltype(auto) get_executor(const Object& object) noexcept
{
return asio::get_associated_executor(object);
}

template <class Object>
decltype(auto) get_allocator(const Object& object) noexcept
{
return asio::get_associated_allocator(object);
}
} // namespace detail

AGRPC_NAMESPACE_END

#endif // AGRPC_DETAIL_ASIO_ASSOCIATION_HPP
#endif // AGRPC_DETAIL_ASSOCIATION_ASIO_HPP
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef AGRPC_DETAIL_ASSOCIATION_UNIFEX_HPP
#define AGRPC_DETAIL_ASSOCIATION_UNIFEX_HPP
#ifndef AGRPC_DETAIL_ASSOCIATION_EXECUTION_HPP
#define AGRPC_DETAIL_ASSOCIATION_EXECUTION_HPP

#include <agrpc/detail/config.hpp>
#include <agrpc/detail/execution.hpp>
Expand All @@ -22,17 +22,8 @@ AGRPC_NAMESPACE_BEGIN()

namespace detail
{
template <class StopToken>
constexpr bool stop_possible(StopToken& stop_token)
{
return stop_token.stop_possible();
}

template <class StopToken>
constexpr bool stop_requested(StopToken& stop_token)
{
return stop_token.stop_requested();
}
template <class CancellationSlot>
inline constexpr bool IS_CANCELLATION_SLOT = false;

template <class T, class = std::false_type>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V = true;
Expand All @@ -43,13 +34,20 @@ using IsStopEverPossibleHelper = std::bool_constant<(T{}.stop_possible())>;
template <class T>
inline constexpr bool IS_STOP_EVER_POSSIBLE_V<T, detail::IsStopEverPossibleHelper<T>> = false;

template <class T>
inline constexpr bool IS_EXECUTOR = exec::scheduler<T>;

template <class T, class...>
using AssociatedAllocatorT = decltype(exec::get_allocator(std::declval<T>()));
using AssociatedExecutorT = decltype(exec::get_scheduler(std::declval<T>()));

template <class T, class...>
using AssociatedExecutorT = decltype(exec::get_executor(std::declval<T>()));
using AssociatedAllocatorT = decltype(exec::get_allocator(std::declval<T>()));

inline const auto& get_executor = exec::get_scheduler;

using exec::get_allocator;
} // namespace detail

AGRPC_NAMESPACE_END

#endif // AGRPC_DETAIL_ASSOCIATION_UNIFEX_HPP
#endif // AGRPC_DETAIL_ASSOCIATION_EXECUTION_HPP
Loading

0 comments on commit b45f41b

Please sign in to comment.