20template <
typename...>
struct always_false {
21 static constexpr bool value =
false;
24template <
typename Allocator,
typename... Args>
25struct first_is_not_allocator :
public std::true_type {};
27template <
typename Allocator,
typename Arg,
typename... Args>
28struct first_is_not_allocator<Allocator, Arg, Args...> {
29 static constexpr bool value =
30 !std::is_same_v<std::remove_cvref_t<Arg>, Allocator>;
33template <
typename Promise,
typename Allocator>
34class BindAllocator :
public Promise {
37 template <
typename... Args>
38 requires(first_is_not_allocator<Allocator, Args...>::value)
39 static void *
operator new(
size_t, Args &&...) {
43 static_assert(always_false<Args...>::value,
44 "Invalid arguments for allocator-bound coroutine");
48 template <
typename... Args>
49 static void *
operator new(
size_t size, Allocator &alloc,
const Args &...) {
50 size_t allocator_offset =
51 (size +
alignof(Allocator) - 1) & ~(
alignof(Allocator) - 1);
52 size_t total_size = allocator_offset +
sizeof(Allocator);
54 Pointer mem = alloc.allocate(total_size);
56 new (mem + allocator_offset) Allocator(alloc);
58 alloc.deallocate(mem, total_size);
64 void operator delete(
void *ptr,
size_t size)
noexcept {
65 size_t allocator_offset =
66 (size +
alignof(Allocator) - 1) & ~(
alignof(Allocator) - 1);
67 size_t total_size = allocator_offset +
sizeof(Allocator);
68 Pointer mem =
static_cast<Pointer
>(ptr);
69 Allocator &alloc = *std::launder(
70 reinterpret_cast<Allocator *
>(mem + allocator_offset));
71 Allocator alloc_copy = std::move(alloc);
73 alloc_copy.deallocate(mem, total_size);
77 using Pointer =
typename std::allocator_traits<Allocator>::pointer;
78 using T = std::remove_pointer_t<Pointer>;
79 static_assert(
sizeof(T) == 1,
"Allocator pointer must point to byte type");
82template <
typename Promise>
83class BindAllocator<Promise, void> :
public Promise {};
85template <
typename Coro>
86class PromiseBase :
public InvokerAdapter<PromiseBase<Coro>, WorkInvoker> {
88 using PromiseType =
typename Coro::promise_type;
91 if (exception_) [[unlikely]] {
93 std::rethrow_exception(exception_);
94 }
catch (
const std::exception &e) {
96 "Unhandled exception in detached coroutine: {}", e.what()));
98 panic_on(
"Unhandled unknown exception in detached coroutine");
103 Coro get_return_object() noexcept {
104 return Coro{std::coroutine_handle<PromiseType>::from_promise(
105 static_cast<PromiseType &
>(*
this))};
108 std::suspend_always initial_suspend() const noexcept {
return {}; }
110 void unhandled_exception() noexcept {
111 exception_ = std::current_exception();
114 struct FinalAwaiter {
115 bool await_ready() const noexcept {
return false; }
117 std::coroutine_handle<>
118 await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
119 auto &self = handle.promise();
121 State expected = self.state_.load(std::memory_order_acquire);
124 if (expected == State::Idle) {
125 return self.caller_handle_;
126 }
else if (expected == State::RunningJoinable) {
127 desired = State::Zombie;
128 }
else if (expected == State::RunningDetached ||
129 expected == State::RunningJoining) {
130 desired = State::Finished;
131 }
else [[unlikely]] {
132 panic_on(std::format(
133 "Invalid coroutine state in final_suspend: {}",
134 static_cast<int>(expected)));
136 }
while (!self.state_.compare_exchange_weak(
137 expected, desired, std::memory_order_acq_rel,
138 std::memory_order_acquire));
140 State prev = expected;
141 if (prev == State::RunningDetached) {
143 return std::noop_coroutine();
144 }
else if (prev == State::RunningJoining) {
145 auto *callback = self.callback_;
147 return std::noop_coroutine();
149 assert(prev == State::RunningJoinable);
150 return std::noop_coroutine();
154 void await_resume() const noexcept {}
157 FinalAwaiter final_suspend() const noexcept {
return {}; }
159 template <
typename T>
160 requires(
requires {
typename std::decay_t<T>::ReturnType; })
161 auto await_transform(T &&value) {
162 return detail::as_awaiter(std::forward<T>(value));
165 template <
typename T> T &&await_transform(T &&value) {
166 return std::forward<T>(value);
171 void mark_running() noexcept {
172 state_.store(State::RunningJoinable, std::memory_order_relaxed);
175 void set_caller_handle(std::coroutine_handle<> handle)
noexcept {
176 caller_handle_ = handle;
179 void request_detach() noexcept {
180 State expected = state_.load(std::memory_order_acquire);
183 if (expected == State::RunningJoinable) {
184 desired = State::RunningDetached;
185 }
else if (expected == State::Zombie) {
186 desired = State::Finished;
187 }
else [[unlikely]] {
189 std::format(
"Invalid coroutine state in request_detach: {}",
190 static_cast<int>(expected)));
192 }
while (!state_.compare_exchange_weak(expected, desired,
193 std::memory_order_acq_rel,
194 std::memory_order_acquire));
196 State prev = expected;
197 if (prev == State::Zombie) {
198 auto h = std::coroutine_handle<PromiseType>::from_promise(
199 static_cast<PromiseType &
>(*
this));
204 bool request_join(Invoker *remote_callback)
noexcept {
205 State expected = state_.load(std::memory_order_acquire);
208 if (expected == State::RunningJoinable) {
209 desired = State::RunningJoining;
210 callback_ = remote_callback;
211 }
else if (expected == State::Zombie) {
212 desired = State::Finished;
213 }
else [[unlikely]] {
215 std::format(
"Invalid coroutine state in request_join: {}",
216 static_cast<int>(expected)));
218 }
while (!state_.compare_exchange_weak(expected, desired,
219 std::memory_order_acq_rel,
220 std::memory_order_acquire));
222 State prev = expected;
223 if (prev == State::Zombie) {
226 assert(prev == State::RunningJoinable);
231 std::exception_ptr exception() noexcept {
return std::move(exception_); }
233 void invoke() noexcept {
234 auto h = std::coroutine_handle<PromiseType>::from_promise(
235 static_cast<PromiseType &
>(*
this));
271 enum class State : uint8_t {
279 static_assert(std::atomic<State>::is_always_lock_free);
281 std::atomic<State> state_ = State::Idle;
283 std::coroutine_handle<> caller_handle_ = std::noop_coroutine();
286 std::exception_ptr exception_;
289template <
typename Allocator>
290class Promise<void, Allocator>
291 :
public BindAllocator<PromiseBase<Coro<void, Allocator>>, Allocator> {
293 void return_void() const noexcept {}
296template <
typename T,
typename Allocator>
298 :
public BindAllocator<PromiseBase<Coro<T, Allocator>>, Allocator> {
300 void return_value(T value) { value_ = std::move(value); }
302 T value() {
return std::move(value_.value()); }
305 std::optional<T> value_;
308template <
typename PromiseType>
struct CoroAwaiterBase {
309 bool await_ready() const noexcept {
return false; }
311 std::coroutine_handle<PromiseType>
312 await_suspend(std::coroutine_handle<> caller_handle)
noexcept {
313 handle_.promise().set_caller_handle(caller_handle);
317 std::coroutine_handle<PromiseType> handle_;
320template <
typename T,
typename Allocator>
322 :
public CoroAwaiterBase<typename Coro<T, Allocator>::promise_type> {
323 using Base = CoroAwaiterBase<typename Coro<T, Allocator>::promise_type>;
325 auto exception = Base::handle_.promise().exception();
326 if (exception) [[unlikely]] {
327 Base::handle_.destroy();
328 std::rethrow_exception(exception);
330 T value = Base::handle_.promise().value();
331 Base::handle_.destroy();
336template <
typename Allocator>
337struct CoroAwaiter<void, Allocator>
338 :
public CoroAwaiterBase<typename Coro<void, Allocator>::promise_type> {
339 using Base = CoroAwaiterBase<typename Coro<void, Allocator>::promise_type>;
340 void await_resume() {
341 auto exception = Base::handle_.promise().exception();
342 Base::handle_.destroy();
343 if (exception) [[unlikely]] {
344 std::rethrow_exception(exception);
349template <
typename T,
typename Allocator>
350inline auto Coro<T, Allocator>::operator
co_await()
noexcept {
351 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.
Helper functions for composing asynchronous operations.
Internal utility classes and functions used by Condy.