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;
89 if (exception_) [[unlikely]] {
91 std::rethrow_exception(exception_);
92 }
catch (
const std::exception &e) {
94 "Unhandled exception in detached coroutine: {}", e.what()));
96 panic_on(
"Unhandled unknown exception in detached coroutine");
101 Coro get_return_object() noexcept {
102 return Coro{std::coroutine_handle<PromiseType>::from_promise(
103 static_cast<PromiseType &
>(*
this))};
106 std::suspend_always initial_suspend() const noexcept {
return {}; }
108 void unhandled_exception() noexcept {
109 exception_ = std::current_exception();
112 struct FinalAwaiter {
113 bool await_ready() const noexcept {
return false; }
115 std::coroutine_handle<>
116 await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
117 auto &self = handle.promise();
118 std::unique_lock lock(self.mutex_);
121 if (self.auto_destroy_) {
122 assert(self.caller_handle_ == std::noop_coroutine());
125 return std::noop_coroutine();
129 if (self.remote_callback_ !=
nullptr) {
130 auto *callback = self.remote_callback_;
131 assert(self.caller_handle_ == std::noop_coroutine());
134 return std::noop_coroutine();
138 self.finished_ =
true;
139 return self.caller_handle_;
142 void await_resume() const noexcept {}
145 FinalAwaiter final_suspend() const noexcept {
return {}; }
148 void request_detach() noexcept {
149 std::lock_guard lock(mutex_);
151 auto_destroy_ =
true;
154 auto handle = std::coroutine_handle<PromiseType>::from_promise(
155 static_cast<PromiseType &
>(*
this));
160 bool register_task_await(Invoker *remote_callback)
noexcept {
161 std::lock_guard lock(mutex_);
165 remote_callback_ = remote_callback;
169 void set_caller_handle(std::coroutine_handle<> handle)
noexcept {
170 caller_handle_ = handle;
173 void set_auto_destroy(
bool auto_destroy)
noexcept {
174 auto_destroy_ = auto_destroy;
177 std::exception_ptr exception() noexcept {
return std::move(exception_); }
179 void invoke() noexcept {
180 auto h = std::coroutine_handle<PromiseType>::from_promise(
181 static_cast<PromiseType &
>(*
this));
187 bool auto_destroy_ =
true;
188 bool finished_ =
false;
189 std::coroutine_handle<> caller_handle_ = std::noop_coroutine();
190 Invoker *remote_callback_ =
nullptr;
191 std::exception_ptr exception_;
194template <
typename Allocator>
195class Promise<void, Allocator>
196 :
public BindAllocator<PromiseBase<Coro<void, Allocator>>, Allocator> {
198 void return_void() const noexcept {}
201template <
typename T,
typename Allocator>
203 :
public BindAllocator<PromiseBase<Coro<T, Allocator>>, Allocator> {
205 void return_value(T value) { value_ = std::move(value); }
207 T value() {
return std::move(value_.value()); }
210 std::optional<T> value_;
213template <
typename PromiseType>
struct CoroAwaiterBase {
214 bool await_ready() const noexcept {
return false; }
216 std::coroutine_handle<PromiseType>
217 await_suspend(std::coroutine_handle<> caller_handle)
noexcept {
218 handle_.promise().set_auto_destroy(
false);
219 handle_.promise().set_caller_handle(caller_handle);
223 std::coroutine_handle<PromiseType> handle_;
226template <
typename T,
typename Allocator>
228 :
public CoroAwaiterBase<typename Coro<T, Allocator>::promise_type> {
229 using Base = CoroAwaiterBase<typename Coro<T, Allocator>::promise_type>;
231 auto exception = Base::handle_.promise().exception();
232 if (exception) [[unlikely]] {
233 Base::handle_.destroy();
234 std::rethrow_exception(exception);
236 T value = Base::handle_.promise().value();
237 Base::handle_.destroy();
242template <
typename Allocator>
243struct CoroAwaiter<void, Allocator>
244 :
public CoroAwaiterBase<typename Coro<void, Allocator>::promise_type> {
245 using Base = CoroAwaiterBase<typename Coro<void, Allocator>::promise_type>;
246 void await_resume() {
247 auto exception = Base::handle_.promise().exception();
248 Base::handle_.destroy();
249 if (exception) [[unlikely]] {
250 std::rethrow_exception(exception);
255template <
typename T,
typename Allocator>
256inline auto Coro<T, Allocator>::operator
co_await()
noexcept {
257 return CoroAwaiter<T, Allocator>{release()};
Polymorphic invocation utilities.
condy::Coro< T, std::pmr::polymorphic_allocator< std::byte > > Coro
Coroutine type using polymorphic allocator.
The main namespace for the Condy library.
Internal utility classes and functions used by Condy.