20template <
typename T,
typename Allocator>
class Coro;
24template <
typename...>
struct always_false {
25 static constexpr bool value =
false;
28template <
typename Allocator,
typename... Args>
29struct first_is_not_allocator :
public std::true_type {};
31template <
typename Allocator,
typename Arg,
typename... Args>
32struct first_is_not_allocator<Allocator, Arg, Args...> {
33 static constexpr bool value =
34 !std::is_same_v<std::remove_cvref_t<Arg>, Allocator>;
37template <
typename Promise,
typename Allocator>
38class BindAllocator :
public Promise {
41 template <
typename... Args>
42 requires(first_is_not_allocator<Allocator, Args...>::value)
43 static void *
operator new(
size_t, Args &&...) {
47 static_assert(always_false<Args...>::value,
48 "Invalid arguments for allocator-bound coroutine");
52 template <
typename... Args>
53 static void *
operator new(
size_t size, Allocator &alloc,
const Args &...) {
54 size_t allocator_offset = align_up(size,
alignof(Allocator));
55 size_t total_size = allocator_offset +
sizeof(Allocator);
57 Pointer mem = alloc.allocate(total_size);
59 new (mem + allocator_offset) Allocator(alloc);
61 alloc.deallocate(mem, total_size);
67 void operator delete(
void *ptr,
size_t size)
noexcept {
68 size_t allocator_offset = align_up(size,
alignof(Allocator));
69 size_t total_size = allocator_offset +
sizeof(Allocator);
70 Pointer mem =
static_cast<Pointer
>(ptr);
71 Allocator &alloc = *std::launder(
72 reinterpret_cast<Allocator *
>(mem + allocator_offset));
73 Allocator alloc_copy = std::move(alloc);
76 alloc_copy.deallocate(mem, total_size);
80 using Pointer =
typename std::allocator_traits<Allocator>::pointer;
81 using T = std::remove_pointer_t<Pointer>;
82 static_assert(
sizeof(T) == 1,
"Allocator pointer must point to byte type");
85template <
typename Promise>
86class BindAllocator<Promise, void> :
public Promise {};
88struct NeverStopToken {
90 template <
typename>
struct callback_type {
91 constexpr explicit callback_type(NeverStopToken,
auto &&) noexcept {}
94 static constexpr bool stop_requested() noexcept {
return false; }
96 static constexpr bool stop_possible() noexcept {
return false; }
98 constexpr bool operator==(NeverStopToken
const &)
const noexcept =
default;
101template <
typename Sender>
class [[nodiscard]] SenderAwaiter {
103 SenderAwaiter(Sender sender)
104 : operation_state_(std::move(sender).connect_impl(Receiver{this})) {}
106 CONDY_DELETE_COPY_MOVE(SenderAwaiter);
109 bool await_ready() const noexcept {
return false; }
111 template <
typename Promise>
112 bool await_suspend(std::coroutine_handle<Promise> handle)
noexcept {
113 operation_state_.start(0);
114 auto h = std::exchange(handle_, handle);
115 return h == std::noop_coroutine();
118 auto await_resume() noexcept {
return std::move(result_); }
123 template <
typename R>
void operator()(R &&result)
noexcept {
124 self->handle_result_(std::forward<R>(result));
126 NeverStopToken get_stop_token() const noexcept {
return {}; }
129 template <
typename R>
void handle_result_(R &&result)
noexcept {
130 result_ = std::forward<R>(result);
131 auto h = std::exchange(handle_,
nullptr);
135 using OperationState = operation_state_t<Sender, Receiver>;
137 std::coroutine_handle<> handle_ = std::noop_coroutine();
138 OperationState operation_state_;
139 typename Sender::ReturnType result_;
142template <
typename Sender>
auto as_awaiter(Sender &&sender) {
143 return SenderAwaiter<std::decay_t<Sender>>(std::forward<Sender>(sender));
146template <
typename Coro>
147class PromiseBase :
public InvokerAdapter<PromiseBase<Coro>, WorkInvoker> {
149 using PromiseType =
typename Coro::promise_type;
152 if (exception_) [[unlikely]] {
154 std::rethrow_exception(exception_);
155 }
catch (
const std::exception &e) {
156 panic_on(std::format(
157 "Unhandled exception in detached coroutine: {}", e.what()));
159 panic_on(
"Unhandled unknown exception in detached coroutine");
164 Coro get_return_object() noexcept {
165 return Coro{std::coroutine_handle<PromiseType>::from_promise(
166 static_cast<PromiseType &
>(*
this))};
169 std::suspend_always initial_suspend() const noexcept {
return {}; }
171 void unhandled_exception() noexcept {
172 exception_ = std::current_exception();
175 struct FinalAwaiter {
176 bool await_ready() const noexcept {
return false; }
178 std::coroutine_handle<>
179 await_suspend(std::coroutine_handle<PromiseType> handle)
noexcept {
180 auto &self = handle.promise();
182 State expected = self.state_.load(std::memory_order_acquire);
185 if (expected == State::Idle) {
186 return self.caller_handle_;
187 }
else if (expected == State::RunningJoinable) {
188 desired = State::Zombie;
189 }
else if (expected == State::RunningDetached ||
190 expected == State::RunningJoining) {
191 desired = State::Finished;
192 }
else [[unlikely]] {
193 panic_on(std::format(
194 "Invalid coroutine state in final_suspend: {}",
195 static_cast<int>(expected)));
197 }
while (!self.state_.compare_exchange_weak(
198 expected, desired, std::memory_order_acq_rel,
199 std::memory_order_acquire));
201 State prev = expected;
202 if (prev == State::RunningDetached) {
204 return std::noop_coroutine();
205 }
else if (prev == State::RunningJoining) {
206 auto *callback = self.callback_;
208 return std::noop_coroutine();
210 assert(prev == State::RunningJoinable);
211 return std::noop_coroutine();
215 void await_resume() const noexcept {}
218 FinalAwaiter final_suspend() const noexcept {
return {}; }
220 template <SenderLike T>
auto await_transform(T &&value) {
221 return as_awaiter(std::forward<T>(value));
224 template <
typename T> T &&await_transform(T &&value) {
225 return std::forward<T>(value);
230 void mark_running() noexcept {
231 state_.store(State::RunningJoinable, std::memory_order_relaxed);
234 void set_caller_handle(std::coroutine_handle<> handle)
noexcept {
235 caller_handle_ = handle;
238 void request_detach() noexcept {
239 State expected = state_.load(std::memory_order_acquire);
242 if (expected == State::RunningJoinable) {
243 desired = State::RunningDetached;
244 }
else if (expected == State::Zombie) {
245 desired = State::Finished;
246 }
else [[unlikely]] {
248 std::format(
"Invalid coroutine state in request_detach: {}",
249 static_cast<int>(expected)));
251 }
while (!state_.compare_exchange_weak(expected, desired,
252 std::memory_order_acq_rel,
253 std::memory_order_acquire));
255 State prev = expected;
256 if (prev == State::Zombie) {
257 auto h = std::coroutine_handle<PromiseType>::from_promise(
258 static_cast<PromiseType &
>(*
this));
263 bool request_join(Invoker *remote_callback)
noexcept {
264 State expected = state_.load(std::memory_order_acquire);
267 if (expected == State::RunningJoinable) {
268 desired = State::RunningJoining;
269 callback_ = remote_callback;
270 }
else if (expected == State::Zombie) {
271 desired = State::Finished;
272 }
else [[unlikely]] {
274 std::format(
"Invalid coroutine state in request_join: {}",
275 static_cast<int>(expected)));
277 }
while (!state_.compare_exchange_weak(expected, desired,
278 std::memory_order_acq_rel,
279 std::memory_order_acquire));
281 State prev = expected;
282 if (prev == State::Zombie) {
285 assert(prev == State::RunningJoinable);
290 std::exception_ptr exception() noexcept {
return std::move(exception_); }
292 void invoke() noexcept {
293 auto h = std::coroutine_handle<PromiseType>::from_promise(
294 static_cast<PromiseType &
>(*
this));
330 enum class State : uint8_t {
338 static_assert(std::atomic<State>::is_always_lock_free);
340 std::atomic<State> state_ = State::Idle;
342 std::coroutine_handle<> caller_handle_ = std::noop_coroutine();
345 std::exception_ptr exception_;
348template <
typename T,
typename Allocator>
350 :
public BindAllocator<PromiseBase<Coro<T, Allocator>>, Allocator> {
352 void return_value(T value) { value_ = std::move(value); }
355 T value() {
return std::move(value_.value()); }
358 std::optional<T> value_;
361template <
typename Allocator>
362class Promise<void, Allocator>
363 :
public BindAllocator<PromiseBase<Coro<void, Allocator>>, Allocator> {
365 void return_void() const noexcept {}
368template <
typename PromiseType>
struct CoroAwaiterBase {
369 bool await_ready() const noexcept {
return false; }
371 std::coroutine_handle<PromiseType>
372 await_suspend(std::coroutine_handle<> caller_handle)
noexcept {
373 handle_.promise().set_caller_handle(caller_handle);
377 std::coroutine_handle<PromiseType> handle_;
380template <
typename T,
typename Allocator>
382 :
public CoroAwaiterBase<typename Coro<T, Allocator>::promise_type> {
383 using Base = CoroAwaiterBase<typename Coro<T, Allocator>::promise_type>;
385 auto exception = Base::handle_.promise().exception();
386 if (exception) [[unlikely]] {
387 Base::handle_.destroy();
388 std::rethrow_exception(exception);
390 T value = Base::handle_.promise().value();
391 Base::handle_.destroy();
396template <
typename Allocator>
397struct CoroAwaiter<void, Allocator>
398 :
public CoroAwaiterBase<typename Coro<void, Allocator>::promise_type> {
399 using Base = CoroAwaiterBase<typename Coro<void, Allocator>::promise_type>;
400 void await_resume() {
401 auto exception = Base::handle_.promise().exception();
402 Base::handle_.destroy();
403 if (exception) [[unlikely]] {
404 std::rethrow_exception(exception);
Coroutine type used to define a coroutine function.
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.