gnss-sim/3rdparty/boost/cobalt/detail/handler.hpp

293 lines
9.2 KiB
C++

// Copyright (c) 2022 Klemens D. Morgenstern
//
// 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_COBALT_HANDLER_HPP
#define BOOST_COBALT_HANDLER_HPP
#include <boost/cobalt/this_coro.hpp>
#include <boost/cobalt/unique_handle.hpp>
#include <boost/cobalt/detail/util.hpp>
#include <boost/cobalt/detail/sbo_resource.hpp>
#include <boost/asio/bind_allocator.hpp>
#include <boost/asio/post.hpp>
#include <boost/system/result.hpp>
#include <memory>
#include <optional>
namespace boost::cobalt
{
namespace detail
{
enum class completed_immediately_t
{
no, maybe, yes, initiating
};
struct completion_handler_noop_executor
{
executor exec;
completed_immediately_t * completed_immediately = nullptr;
template<typename Fn>
void execute(Fn && fn) const
{
// only allow it when we're still initializing
if (completed_immediately &&
((*completed_immediately == completed_immediately_t::initiating)
|| (*completed_immediately == completed_immediately_t::maybe)))
{
// only use this indicator if the fn will actually call our completion-handler
// otherwise this was a single op in a composed operation
*completed_immediately = completed_immediately_t::maybe;
fn();
// yes means completion_handler::operator() was called, so we're good.
if (*completed_immediately != completed_immediately_t::yes)
*completed_immediately = completed_immediately_t::initiating;
}
else
{
asio::post(exec, std::forward<Fn>(fn));
}
}
friend bool operator==(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
{
return true;
}
friend bool operator!=(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
{
return false;
}
completion_handler_noop_executor(const completion_handler_noop_executor & rhs) noexcept = default;
completion_handler_noop_executor(cobalt::executor inner, completed_immediately_t * completed_immediately)
: exec(std::move(inner)), completed_immediately(completed_immediately)
{
}
};
struct completion_handler_base
{
using cancellation_slot_type = asio::cancellation_slot;
cancellation_slot_type cancellation_slot ;
cancellation_slot_type get_cancellation_slot() const noexcept
{
return cancellation_slot ;
}
using executor_type = executor;
const executor_type & executor_ ;
const executor_type & get_executor() const noexcept
{
return executor_ ;
}
#if !defined(BOOST_COBALT_NO_PMR)
using allocator_type = pmr::polymorphic_allocator<void>;
pmr::polymorphic_allocator<void> allocator ;
allocator_type get_allocator() const noexcept
{
return allocator ;
}
#else
using allocator_type = detail::sbo_allocator<void>;
detail::sbo_allocator<void> allocator ;
allocator_type get_allocator() const noexcept
{
return allocator ;
}
#endif
using immediate_executor_type = completion_handler_noop_executor;
completed_immediately_t * completed_immediately = nullptr;
immediate_executor_type get_immediate_executor() const noexcept
{
return {get_executor(), completed_immediately};
}
template<typename Promise>
requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
completion_handler_base(std::coroutine_handle<Promise> h,
completed_immediately_t * completed_immediately = nullptr)
: cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
executor_(h.promise().get_executor()),
#if !defined(BOOST_COBALT_NO_PMR)
allocator(asio::get_associated_allocator(h.promise(), this_thread::get_allocator())),
#else
allocator(detail::get_null_sbo_resource()),
#endif
completed_immediately(completed_immediately)
{
}
#if !defined(BOOST_COBALT_NO_PMR)
template<typename Promise>
requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
completion_handler_base(std::coroutine_handle<Promise> h,
pmr::memory_resource * resource,
completed_immediately_t * completed_immediately = nullptr)
: cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
executor_(h.promise().get_executor()),
allocator(resource),
completed_immediately(completed_immediately)
{
}
#else
template<typename Promise>
requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
completion_handler_base(std::coroutine_handle<Promise> h,
detail::sbo_resource * resource,
completed_immediately_t * completed_immediately = nullptr)
: cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
executor_(h.promise().get_executor()),
allocator(resource),
completed_immediately(completed_immediately)
{
}
#endif
};
template<typename Handler>
void assign_cancellation(std::coroutine_handle<void>, Handler &&) {}
template<typename Promise, typename Handler>
void assign_cancellation(std::coroutine_handle<Promise> h, Handler && func)
{
if constexpr (requires {h.promise().get_cancellation_slot();})
if (h.promise().get_cancellation_slot().is_connected())
h.promise().get_cancellation_slot().assign(std::forward<Handler>(func));
}
template<typename Promise>
const executor &
get_executor(std::coroutine_handle<Promise> h)
{
if constexpr (requires {h.promise().get_executor();})
{
static_assert(std::same_as<decltype(h.promise().get_executor()),
const executor &>,
"for performance reasons, the get_executor function on a promise must return a const reference");
return h.promise().get_executor();
}
else
return this_thread::get_executor();
}
inline const executor &
get_executor(std::coroutine_handle<>)
{
return this_thread::get_executor();
}
}
template<typename ... Args>
struct handler
{
void operator()(Args ... args)
{
result.emplace(static_cast<Args>(args)...);
}
handler(std::optional<std::tuple<Args...>> &result) : result(result) {}
private:
std::optional<std::tuple<Args...>> &result;
};
template<typename ... Args>
handler(std::optional<std::tuple<Args...>> &result) -> handler<Args...>;
template<typename ... Args>
struct completion_handler : detail::completion_handler_base
{
completion_handler(completion_handler && ) = default;
template<typename Promise>
completion_handler(std::coroutine_handle<Promise> h,
std::optional<std::tuple<Args...>> &result,
detail::completed_immediately_t * completed_immediately = nullptr
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
, const boost::source_location & loc = BOOST_CURRENT_LOCATION
#endif
) : completion_handler_base(h, completed_immediately),
self(h.address()), result(result)
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
, loc_(loc)
#endif
{
}
#if !defined(BOOST_COBALT_NO_PMR)
template<typename Promise>
completion_handler(std::coroutine_handle<Promise> h,
std::optional<std::tuple<Args...>> &result,
pmr::memory_resource * resource,
detail::completed_immediately_t * completed_immediately = nullptr
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
, const boost::source_location & loc = BOOST_CURRENT_LOCATION
#endif
) : completion_handler_base(h, resource, completed_immediately),
self(h.address()), result(result)
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
, loc_(loc)
#endif
{
}
#else
template<typename Promise>
completion_handler(std::coroutine_handle<Promise> h,
std::optional<std::tuple<Args...>> &result,
detail::sbo_resource * resource,
detail::completed_immediately_t * completed_immediately = nullptr)
: completion_handler_base(h, resource, completed_immediately),
self(h.address()), result(result)
{
}
#endif
void operator()(Args ... args)
{
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
BOOST_ASIO_HANDLER_LOCATION((loc_.file_name(), loc_.line(), loc_.function_name()));
#endif
result.emplace(std::move(args)...);
BOOST_ASSERT(this->self != nullptr);
auto p = this->self.release();
if (completed_immediately != nullptr
&& *completed_immediately == detail::completed_immediately_t::maybe)
{
*completed_immediately = detail::completed_immediately_t::yes;
return;
}
std::move(p)();
}
using result_type = std::optional<std::tuple<Args...>>;
~completion_handler()
{
if (self && completed_immediately
&& *completed_immediately == detail::completed_immediately_t::initiating
&& std::uncaught_exceptions() > 0)
self.release();
}
private:
unique_handle<void> self;
std::optional<std::tuple<Args...>> &result;
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
boost::source_location loc_;
#endif
};
};
#endif //BOOST_COBALT_HANDLER_HPP