341 lines
9.7 KiB
C++
341 lines
9.7 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* https://llvm.org/LICENSE.txt
|
|
*
|
|
* 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.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <unifex/coroutine.hpp>
|
|
#include <unifex/std_concepts.hpp>
|
|
#include <unifex/type_traits.hpp>
|
|
#include <unifex/tag_invoke.hpp>
|
|
#include <unifex/type_index.hpp>
|
|
|
|
#include <exception>
|
|
|
|
#include <unifex/detail/prologue.hpp>
|
|
|
|
namespace unifex {
|
|
|
|
namespace _visit_continuations_cpo {
|
|
inline const struct _fn {
|
|
#if !UNIFEX_NO_COROUTINES
|
|
template(typename Promise, typename Func)
|
|
(requires (!same_as<Promise, void>) AND callable<_fn, Promise&, Func>)
|
|
friend void tag_invoke(
|
|
_fn cpo,
|
|
coro::coroutine_handle<Promise> h,
|
|
Func&& func) noexcept(is_nothrow_callable_v<_fn, Promise&, Func>) {
|
|
cpo(h.promise(), (Func &&) func);
|
|
}
|
|
#endif // UNIFEX_NO_COROUTINES
|
|
|
|
template(typename Continuation, typename Func)
|
|
(requires tag_invocable<_fn, const Continuation&, Func>)
|
|
void operator()(const Continuation& c, Func&& func) const
|
|
noexcept(is_nothrow_tag_invocable_v<
|
|
_fn,
|
|
const Continuation&,
|
|
Func&&>) {
|
|
static_assert(
|
|
std::is_void_v<tag_invoke_result_t<
|
|
_fn,
|
|
const Continuation&,
|
|
Func&&>>,
|
|
"tag_invoke() overload for visit_continuations() must return void");
|
|
return tag_invoke(_fn{}, c, (Func &&) func);
|
|
}
|
|
|
|
template(typename Continuation, typename Func)
|
|
(requires (!tag_invocable<_fn, const Continuation&, Func>))
|
|
void operator()(const Continuation&, Func&&) const noexcept {}
|
|
} visit_continuations {};
|
|
} // namespace _visit_continuations_cpo
|
|
using _visit_continuations_cpo::visit_continuations;
|
|
|
|
#if !UNIFEX_NO_COROUTINES
|
|
namespace _ch {
|
|
template <typename Promise = void>
|
|
struct continuation_handle;
|
|
} // namespace _ch
|
|
using _ch::continuation_handle;
|
|
#endif
|
|
|
|
namespace _ci {
|
|
class continuation_info;
|
|
|
|
struct _continuation_info_vtable {
|
|
using callback_t = void(const continuation_info&, void*);
|
|
using visitor_t = void(const void*, callback_t*, void*);
|
|
using type_index_getter_t = type_index() noexcept;
|
|
|
|
type_index_getter_t* typeIndexGetter_;
|
|
visitor_t* visit_;
|
|
};
|
|
|
|
inline type_index _default_type_index_getter() noexcept {
|
|
return type_id<void>();
|
|
}
|
|
|
|
inline void _default_visit(const void*, _continuation_info_vtable::callback_t*, void*) {
|
|
}
|
|
|
|
template <typename F>
|
|
void _invoke_visitor(const continuation_info& info, void* data) {
|
|
std::invoke(*static_cast<std::add_pointer_t<F>>(data), info);
|
|
}
|
|
|
|
class continuation_info {
|
|
public:
|
|
continuation_info() = default;
|
|
|
|
template <typename Continuation>
|
|
static continuation_info from_continuation(const Continuation& c) noexcept;
|
|
|
|
static continuation_info from_continuation(const continuation_info& c) noexcept {
|
|
return c;
|
|
}
|
|
|
|
#if !UNIFEX_NO_COROUTINES
|
|
template <typename Promise>
|
|
static continuation_info from_continuation(
|
|
const continuation_handle<Promise>& c) noexcept {
|
|
return continuation_info{c.handle().address(), c.vtable_};
|
|
}
|
|
#endif
|
|
|
|
type_index type() const noexcept {
|
|
return vtable_->typeIndexGetter_();
|
|
}
|
|
|
|
const void* address() const noexcept {
|
|
return address_;
|
|
}
|
|
|
|
template <typename F>
|
|
friend void
|
|
tag_invoke(tag_t<visit_continuations>, const continuation_info& c, F&& f) {
|
|
// Any const that gets stripped by the const_cast below gets reapplied by the
|
|
// static_cast in _invoke_visitor.
|
|
c.vtable_->visit_(
|
|
c.address_,
|
|
&_invoke_visitor<F>,
|
|
const_cast<void*>(static_cast<const void*>(std::addressof(f))));
|
|
}
|
|
|
|
private:
|
|
explicit continuation_info(
|
|
const void* address,
|
|
const _continuation_info_vtable* vtable) noexcept
|
|
: address_(address)
|
|
, vtable_(vtable) {}
|
|
|
|
inline static constexpr _continuation_info_vtable default_vtable_ {
|
|
&_default_type_index_getter,
|
|
&_default_visit
|
|
};
|
|
|
|
const void* address_{nullptr};
|
|
const _continuation_info_vtable* vtable_{&default_vtable_};
|
|
};
|
|
|
|
template <typename Continuation>
|
|
type_index _type_index_getter_for() noexcept {
|
|
return type_id<Continuation>();
|
|
}
|
|
|
|
template <typename Continuation>
|
|
void _visit_for(const void* address, _continuation_info_vtable::callback_t* cb, void* data) {
|
|
visit_continuations(
|
|
*static_cast<const Continuation*>(address),
|
|
[cb, data](const auto& continuation) {
|
|
cb(continuation_info::from_continuation(continuation), data);
|
|
});
|
|
}
|
|
|
|
template <typename Continuation>
|
|
inline constexpr _continuation_info_vtable _vtable_for {
|
|
&_type_index_getter_for<Continuation>,
|
|
&_visit_for<Continuation>
|
|
};
|
|
|
|
template <typename Continuation>
|
|
inline continuation_info continuation_info::from_continuation(
|
|
const Continuation& r) noexcept {
|
|
return continuation_info{static_cast<const void*>(std::addressof(r)),
|
|
&_vtable_for<Continuation>};
|
|
}
|
|
} // namespace _ci
|
|
using _ci::continuation_info;
|
|
|
|
#if !UNIFEX_NO_COROUTINES
|
|
namespace _ch {
|
|
[[noreturn]] inline coro::coroutine_handle<> _default_done_callback(void*) noexcept {
|
|
std::terminate();
|
|
}
|
|
|
|
struct _continuation_handle_vtable : _ci::_continuation_info_vtable {
|
|
using done_callback_t = coro::coroutine_handle<>(void*) noexcept;
|
|
done_callback_t* doneCallback_;
|
|
};
|
|
|
|
template <>
|
|
struct continuation_handle<void> {
|
|
continuation_handle() = default;
|
|
|
|
// Because of a detail in the concepts emulation macros, it is not possible to
|
|
// forward-declare a constrained function and provide its definition later. So
|
|
// below we define a constrained constructor that trivially dispatches to an
|
|
// unconstrained implementation defined elsewhere.
|
|
template (typename Promise)
|
|
(requires (!same_as<Promise, void>))
|
|
/*implicit*/ continuation_handle(coro::coroutine_handle<Promise> continuation) noexcept
|
|
: continuation_handle(0, (coro::coroutine_handle<Promise>&&) continuation)
|
|
{}
|
|
|
|
explicit operator bool() const noexcept {
|
|
return handle_ != nullptr;
|
|
}
|
|
|
|
coro::coroutine_handle<> handle() const noexcept {
|
|
return handle_;
|
|
}
|
|
|
|
void resume() {
|
|
handle_.resume();
|
|
}
|
|
|
|
coro::coroutine_handle<> done() const noexcept {
|
|
return vtable_->doneCallback_(handle_.address());
|
|
}
|
|
|
|
continuation_info info() const noexcept {
|
|
return continuation_info::from_continuation(*this);
|
|
}
|
|
|
|
template <typename F>
|
|
friend void
|
|
tag_invoke(tag_t<visit_continuations>, const continuation_handle<>& c, F&& f) {
|
|
// Any const that gets stripped by the const_cast below gets reapplied by the
|
|
// static_cast in _invoke_visitor.
|
|
c.vtable_->visit_(
|
|
c.handle_.address(),
|
|
&_ci::_invoke_visitor<F>,
|
|
const_cast<void*>(static_cast<const void*>(std::addressof(f))));
|
|
}
|
|
|
|
private:
|
|
friend continuation_info;
|
|
|
|
inline static constexpr _continuation_handle_vtable default_vtable_ {
|
|
{
|
|
&_ci::_default_type_index_getter,
|
|
&_ci::_default_visit,
|
|
},
|
|
&_default_done_callback
|
|
};
|
|
|
|
template <typename Promise>
|
|
continuation_handle(int, coro::coroutine_handle<Promise> continuation) noexcept;
|
|
|
|
coro::coroutine_handle<> handle_{};
|
|
const _continuation_handle_vtable* vtable_ {&default_vtable_};
|
|
};
|
|
|
|
template <typename Promise>
|
|
void _visit_for(const void* address, _ci::_continuation_info_vtable::callback_t* cb, void* data) {
|
|
visit_continuations(
|
|
const_cast<const Promise&>(
|
|
coro::coroutine_handle<Promise>::from_address(
|
|
const_cast<void*>(address)).promise()),
|
|
[cb, data](const auto& continuation) {
|
|
cb(continuation_info::from_continuation(continuation), data);
|
|
});
|
|
}
|
|
|
|
template <typename Promise>
|
|
coro::coroutine_handle<> _done_callback_for(void* address) noexcept {
|
|
return coro::coroutine_handle<Promise>::from_address(address).promise().unhandled_done();
|
|
}
|
|
|
|
template <typename Promise>
|
|
inline constexpr _continuation_handle_vtable _vtable_for {
|
|
{
|
|
&_ci::_type_index_getter_for<Promise>,
|
|
&_visit_for<Promise>,
|
|
},
|
|
&_done_callback_for<Promise>
|
|
};
|
|
|
|
template <typename Promise>
|
|
continuation_handle<void>::continuation_handle(int, coro::coroutine_handle<Promise> continuation) noexcept
|
|
: handle_((coro::coroutine_handle<Promise>&&) continuation)
|
|
, vtable_(&_vtable_for<Promise>)
|
|
{}
|
|
|
|
template <typename Promise>
|
|
struct continuation_handle {
|
|
continuation_handle() = default;
|
|
|
|
/*implicit*/ continuation_handle(coro::coroutine_handle<Promise> continuation) noexcept
|
|
: self_((coro::coroutine_handle<Promise>&&) continuation)
|
|
{}
|
|
|
|
explicit operator bool() const noexcept {
|
|
return !!self_;
|
|
}
|
|
|
|
/*implicit*/ operator continuation_handle<>() const noexcept {
|
|
return self_;
|
|
}
|
|
|
|
coro::coroutine_handle<Promise> handle() const noexcept {
|
|
return coro::coroutine_handle<Promise>::from_address(
|
|
self_.handle().address());
|
|
}
|
|
|
|
void resume() {
|
|
self_.resume();
|
|
}
|
|
|
|
Promise& promise() const noexcept {
|
|
return handle().promise();
|
|
}
|
|
|
|
coro::coroutine_handle<> done() const noexcept {
|
|
return self_.done();
|
|
}
|
|
|
|
continuation_info info() const noexcept {
|
|
return self_.info();
|
|
}
|
|
|
|
template <typename F>
|
|
friend void
|
|
tag_invoke(tag_t<visit_continuations>, const continuation_handle<Promise>& c, F&& f) {
|
|
visit_continuations(c.self_, (F&&) f);
|
|
}
|
|
|
|
private:
|
|
friend continuation_info;
|
|
|
|
continuation_handle<> self_;
|
|
};
|
|
} // namespace _ch
|
|
using _ch::continuation_handle;
|
|
#endif
|
|
|
|
} // namespace unifex
|
|
|
|
#include <unifex/detail/epilogue.hpp>
|