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

276 lines
6.8 KiB
C++

// Copyright (c) 2023 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_DETAIL_FORK_HPP
#define BOOST_COBALT_DETAIL_FORK_HPP
#include <boost/cobalt/config.hpp>
#include <boost/cobalt/detail/await_result_helper.hpp>
#include <boost/cobalt/detail/util.hpp>
#include <boost/cobalt/this_thread.hpp>
#include <boost/cobalt/unique_handle.hpp>
#if defined(BOOST_COBALT_NO_PMR)
#include <boost/cobalt/detail/monotonic_resource.hpp>
#endif
#include <boost/asio/cancellation_signal.hpp>
#include <boost/intrusive_ptr.hpp>
#include <coroutine>
namespace boost::cobalt::detail
{
struct fork
{
fork() = default;
struct shared_state
{
#if !defined(BOOST_COBALT_NO_PMR)
pmr::monotonic_buffer_resource resource;
template<typename ... Args>
shared_state(Args && ... args)
: resource(std::forward<Args>(args)...,
this_thread::get_default_resource())
{
}
#else
detail::monotonic_resource resource;
template<typename ... Args>
shared_state(Args && ... args)
: resource(std::forward<Args>(args)...)
{
}
#endif
// the coro awaiting the fork statement, e.g. awaiting race
unique_handle<void> coro;
std::size_t use_count = 0u;
friend void intrusive_ptr_add_ref(shared_state * st) {st->use_count++;}
friend void intrusive_ptr_release(shared_state * st)
{
if (st->use_count-- == 1u)
st->coro.reset();
}
bool outstanding_work() {return use_count != 0u;}
const executor * exec = nullptr;
bool wired_up() {return exec != nullptr;}
using executor_type = executor;
const executor_type & get_executor() const
{
BOOST_ASSERT(exec != nullptr);
return *exec;
}
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
boost::source_location loc;
#endif
};
template<typename std::size_t BufferSize>
struct static_shared_state : private std::array<char, BufferSize>, shared_state
{
static_shared_state() : shared_state{std::array<char, BufferSize>::data(),
std::array<char, BufferSize>::size()}
{}
};
struct wired_up_t {};
constexpr static wired_up_t wired_up{};
struct set_transaction_function
{
void * begin_transaction_this = nullptr;
void (*begin_transaction_func)(void*) = nullptr;
template<typename BeginTransaction>
set_transaction_function(BeginTransaction & transaction)
: begin_transaction_this(&transaction)
, begin_transaction_func(
+[](void * ptr)
{
(*static_cast<BeginTransaction*>(ptr))();
})
{
}
};
struct promise_type
{
template<typename State, typename ... Rest>
void * operator new(const std::size_t size, State & st, Rest &&...)
{
return st.resource.allocate(size);
}
void operator delete(void *) noexcept {}
template<typename ... Rest>
promise_type(shared_state & st, Rest & ...)
: state(&st)
{
}
intrusive_ptr<shared_state> state;
asio::cancellation_slot cancel;
using executor_type = executor;
const executor_type & get_executor() const { return state->get_executor(); }
#if defined(BOOST_COBALT_NO_PMR)
using allocator_type = detail::monotonic_allocator<void>;
const allocator_type get_allocator() const { return &state->resource; }
#else
using allocator_type = pmr::polymorphic_allocator<void>;
const allocator_type get_allocator() const { return &state->resource; }
#endif
using cancellation_slot_type = asio::cancellation_slot;
cancellation_slot_type get_cancellation_slot() const { return cancel; }
constexpr static std::suspend_never initial_suspend() noexcept {return {};}
struct final_awaitable
{
promise_type * self;
bool await_ready() noexcept
{
return self->state->use_count != 1u;
}
std::coroutine_handle<void> await_suspend(std::coroutine_handle<promise_type> h) noexcept
{
auto pp = h.promise().state.detach();
#if defined(BOOST_COBALT_NO_SELF_DELETE)
h.promise().~promise_type();
#else
// mem is in a monotonic_resource, this is fine on msvc- gcc doesn't like it though
h.destroy();
#endif
pp->use_count--;
BOOST_ASSERT(pp->use_count == 0u);
if (pp->coro)
return pp->coro.release();
else
return std::noop_coroutine();
}
constexpr static void await_resume() noexcept {}
};
final_awaitable final_suspend() noexcept
{
if (cancel.is_connected())
cancel.clear();
return final_awaitable{this};
}
void return_void()
{
}
template<awaitable<promise_type> Aw>
struct wrapped_awaitable
{
Aw & aw;
constexpr static bool await_ready() noexcept
{
return false;
}
auto await_suspend(std::coroutine_handle<promise_type> h)
{
BOOST_ASSERT(h.promise().state->wired_up());
#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
if constexpr (requires {aw.await_suspend(h, boost::source_location ());})
return aw.await_suspend(h, h.promise().state->loc);
#endif
return aw.await_suspend(h);
}
auto await_resume()
{
return aw.await_resume();
}
};
template<awaitable<promise_type> Aw>
auto await_transform(Aw & aw)
{
return wrapped_awaitable<Aw>{aw};
}
struct wired_up_awaitable
{
promise_type * promise;
bool await_ready() const noexcept
{
return promise->state->wired_up();
}
void await_suspend(std::coroutine_handle<promise_type>)
{
}
constexpr static void await_resume() noexcept {}
};
auto await_transform(wired_up_t)
{
return wired_up_awaitable{this};
}
auto await_transform(set_transaction_function sf)
{
begin_transaction_this = sf.begin_transaction_this;
begin_transaction_func = sf.begin_transaction_func;
return std::suspend_never();
}
auto await_transform(asio::cancellation_slot slot)
{
this->cancel = slot;
return std::suspend_never();
}
[[noreturn]] void unhandled_exception() noexcept {std::terminate();}
void * begin_transaction_this = nullptr;
void (*begin_transaction_func)(void*) = nullptr;
void begin_transaction()
{
if (begin_transaction_this)
begin_transaction_func(begin_transaction_this);
}
fork get_return_object()
{
return this;
}
};
[[nodiscard]] bool done() const
{
return ! handle_ || handle_.done();
}
auto release() -> std::coroutine_handle<promise_type>
{
return handle_.release();
}
private:
fork(promise_type * pt) : handle_(pt) {}
unique_handle<promise_type> handle_;
};
}
#endif //BOOST_COBALT_DETAIL_FORK_HPP