18template <
typename...>
struct always_false {
19 static constexpr bool value =
false;
22template <
typename Allocator,
typename... Args>
23struct first_is_not_allocator :
public std::true_type {};
25template <
typename Allocator,
typename Arg,
typename... Args>
26struct first_is_not_allocator<Allocator, Arg, Args...> {
27 static constexpr bool value =
28 !std::is_same_v<std::remove_cvref_t<Arg>, Allocator>;
31template <
typename Promise,
typename Allocator>
32class BindAllocator :
public Promise {
35 template <
typename... Args>
36 requires(first_is_not_allocator<Allocator, Args...>::value)
37 static void *
operator new(
size_t, Args &&...) {
41 static_assert(always_false<Args...>::value,
42 "Invalid arguments for allocator-bound coroutine");
46 template <
typename... Args>
47 static void *
operator new(
size_t size, Allocator &alloc,
const Args &...) {
48 size_t allocator_offset =
49 (size +
alignof(Allocator) - 1) & ~(
alignof(Allocator) - 1);
50 size_t total_size = allocator_offset +
sizeof(Allocator);
52 Pointer mem = alloc.allocate(total_size);
54 new (mem + allocator_offset) Allocator(alloc);
56 alloc.deallocate(mem, total_size);
62 void operator delete(
void *ptr,
size_t size)
noexcept {
63 size_t allocator_offset =
64 (size +
alignof(Allocator) - 1) & ~(
alignof(Allocator) - 1);
65 size_t total_size = allocator_offset +
sizeof(Allocator);
66 Pointer mem =
static_cast<Pointer
>(ptr);
68 *
reinterpret_cast<Allocator *
>(mem + allocator_offset);
69 Allocator alloc_copy = std::move(alloc);
71 alloc_copy.deallocate(mem, total_size);
75 using Pointer =
typename std::allocator_traits<Allocator>::pointer;
76 using T = std::remove_pointer_t<Pointer>;
77 static_assert(
sizeof(T) == 1,
"Allocator pointer must point to byte type");
80template <
typename Promise>
81class BindAllocator<Promise, void> :
public Promise {};
83template <
typename Coro>
84class PromiseBase :
public InvokerAdapter<PromiseBase<Coro>, WorkInvoker> {
86 using PromiseType =
typename Coro::promise_type;
90 panic_on(
"Unhandled exception in detached coroutine!!!");
94 Coro get_return_object() {
95 return Coro{std::coroutine_handle<PromiseType>::from_promise(
96 static_cast<PromiseType &
>(*
this))};
99 std::suspend_always initial_suspend() noexcept {
return {}; }
101 void unhandled_exception() { exception_ = std::current_exception(); }
103 struct FinalAwaiter {
104 bool await_ready() noexcept {
return false; }
106 std::coroutine_handle<>
107 await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
108 auto &self = handle.promise();
109 std::unique_lock lock(self.mutex_);
112 if (self.auto_destroy_) {
113 assert(self.caller_handle_ == std::noop_coroutine());
116 return std::noop_coroutine();
120 if (self.remote_callback_ !=
nullptr) {
121 auto *callback = self.remote_callback_;
122 assert(self.caller_handle_ == std::noop_coroutine());
125 return std::noop_coroutine();
129 self.finished_ =
true;
130 return self.caller_handle_;
133 void await_resume() noexcept {}
136 FinalAwaiter final_suspend() noexcept {
return {}; }
139 void request_detach() noexcept {
140 std::lock_guard lock(mutex_);
142 auto_destroy_ =
true;
145 auto handle = std::coroutine_handle<PromiseType>::from_promise(
146 static_cast<PromiseType &
>(*
this));
151 bool register_task_await(Invoker *remote_callback)
noexcept {
152 std::lock_guard lock(mutex_);
156 remote_callback_ = remote_callback;
160 void set_caller_handle(std::coroutine_handle<> handle)
noexcept {
161 caller_handle_ = handle;
164 void set_auto_destroy(
bool auto_destroy)
noexcept {
165 auto_destroy_ = auto_destroy;
168 std::exception_ptr &exception() &
noexcept {
return exception_; }
169 std::exception_ptr &&exception() &&
noexcept {
170 return std::move(exception_);
174 auto h = std::coroutine_handle<PromiseType>::from_promise(
175 static_cast<PromiseType &
>(*
this));
181 std::coroutine_handle<> caller_handle_ = std::noop_coroutine();
182 bool auto_destroy_ =
true;
183 bool finished_ =
false;
184 Invoker *remote_callback_ =
nullptr;
186 std::exception_ptr exception_;
189template <
typename Allocator>
190class Promise<void, Allocator>
191 :
public BindAllocator<PromiseBase<Coro<void, Allocator>>, Allocator> {
193 void return_void() noexcept {}
196template <
typename T,
typename Allocator>
198 :
public BindAllocator<PromiseBase<Coro<T, Allocator>>, Allocator> {
200 void return_value(T value) { value_ = std::move(value); }
202 T &value() &
noexcept {
return value_.value(); }
203 T &&value() &&
noexcept {
return std::move(value_).value(); }
206 std::optional<T> value_;
209template <
typename PromiseType>
struct CoroAwaiterBase {
210 bool await_ready() const noexcept {
return false; }
212 std::coroutine_handle<PromiseType>
213 await_suspend(std::coroutine_handle<> caller_handle)
noexcept {
214 handle_.promise().set_auto_destroy(
false);
215 handle_.promise().set_caller_handle(caller_handle);
219 std::coroutine_handle<PromiseType> handle_;
222template <
typename T,
typename Allocator>
224 :
public CoroAwaiterBase<typename Coro<T, Allocator>::promise_type> {
225 using Base = CoroAwaiterBase<typename Coro<T, Allocator>::promise_type>;
227 auto exception = std::move(Base::handle_.promise()).exception();
228 if (exception) [[unlikely]] {
229 Base::handle_.destroy();
230 std::rethrow_exception(exception);
232 T value = std::move(Base::handle_.promise()).value();
233 Base::handle_.destroy();
238template <
typename Allocator>
239struct CoroAwaiter<void, Allocator>
240 :
public CoroAwaiterBase<typename Coro<void, Allocator>::promise_type> {
241 using Base = CoroAwaiterBase<typename Coro<void, Allocator>::promise_type>;
242 void await_resume() {
243 auto exception = std::move(Base::handle_.promise()).exception();
244 Base::handle_.destroy();
245 if (exception) [[unlikely]] {
246 std::rethrow_exception(exception);
251template <
typename T,
typename Allocator>
252inline auto Coro<T, Allocator>::operator
co_await()
noexcept {
253 return CoroAwaiter<T, Allocator>{release()};
Polymorphic invocation utilities.
condy::Coro< T, std::pmr::polymorphic_allocator< std::byte > > Coro
Coroutine type using polymorphic allocator.
Definition pmr.hpp:26
The main namespace for the Condy library.
Definition condy.hpp:28
Internal utility classes and functions used by Condy.