libcopp 2.3.2
Loading...
Searching...
No Matches
std_coroutine_common.h
Go to the documentation of this file.
1// Copyright 2026 owent
2
3#pragma once
4
6#include <libcopp/utils/config/libcopp_build_features.h>
8
9// clang-format off
10#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
11// clang-format on
12
13#include <assert.h>
14#include <cstddef>
15#include <list>
16#include <memory>
17#include <type_traits>
18#include <unordered_map>
19
20#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
21# include <exception>
22# include <list>
23#endif
24
25#if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
26# include <variant>
27#endif
28
29#ifdef __cpp_impl_three_way_comparison
30# include <compare>
31#endif
32
33// clang-format off
34#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
35// clang-format on
36
39
40#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
41
42LIBCOPP_COPP_NAMESPACE_BEGIN
43
44enum class LIBCOPP_COPP_API_HEAD_ONLY promise_status : uint8_t {
45 kInvalid = 0,
46 kCreated = 1,
47 kRunning = 2,
48 kDone = 3,
49 kCancle = 4,
50 kKilled = 5,
51 kTimeout = 6,
52};
53
54enum class LIBCOPP_COPP_API_HEAD_ONLY promise_flag : uint8_t {
55 kDestroying = 0,
56 kFinalSuspend = 1,
57 kInternalWaitting = 2,
58 kHasReturned = 3,
59 kMax,
60};
61
62template <class TVALUE, bool ALLOW_MOVE>
63struct LIBCOPP_COPP_API_HEAD_ONLY _multiple_callers_constructor;
64
65template <class TVALUE>
66struct LIBCOPP_COPP_API_HEAD_ONLY _multiple_callers_constructor<TVALUE, true> {
67 LIBCOPP_UTIL_FORCEINLINE static TVALUE &&return_value(TVALUE &input) noexcept { return std::move(input); }
68};
69
70template <class TVALUE>
71struct LIBCOPP_COPP_API_HEAD_ONLY _multiple_callers_constructor<TVALUE, false> {
72 LIBCOPP_UTIL_FORCEINLINE static const TVALUE &return_value(TVALUE &input) noexcept { return input; }
73};
74
75template <class TVALUE>
76struct LIBCOPP_COPP_API_HEAD_ONLY multiple_callers_constructor
77 : public _multiple_callers_constructor<
78 TVALUE, !(std::is_pointer<TVALUE>::value || std::is_reference<TVALUE>::value ||
79 !std::is_move_constructible<TVALUE>::value ||
80 (std::is_trivially_copyable<TVALUE>::value && sizeof(TVALUE) <= sizeof(std::max_align_t)))> {};
81
82class promise_base_type;
83
84# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
85template <class T>
86concept DerivedPromiseBaseType = std::is_base_of<promise_base_type, T>::value;
87# endif
88
89class promise_caller_manager {
90 private:
91 promise_caller_manager(const promise_caller_manager &) = delete;
92 promise_caller_manager(promise_caller_manager &&) = delete;
93 promise_caller_manager &operator=(const promise_caller_manager &) = delete;
94 promise_caller_manager &operator=(promise_caller_manager &&) = delete;
95
96 public:
97 using type_erased_handle_type = LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<>;
98 struct LIBCOPP_COPP_API_HEAD_ONLY handle_delegate {
99 type_erased_handle_type handle;
100 promise_base_type *promise;
101
102# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
103 template <DerivedPromiseBaseType TPROMISE>
104# else
105 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
106# endif
107 explicit handle_delegate(
108 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &origin_handle) noexcept
109 : handle{origin_handle} {
110 if (handle) {
111 promise = &origin_handle.promise();
112 } else {
113 promise = nullptr;
114 }
115 }
116
117 explicit handle_delegate(std::nullptr_t) noexcept : handle{nullptr}, promise{nullptr} {}
118
119 friend inline bool operator==(const handle_delegate &l, const handle_delegate &r) noexcept {
120 return l.handle == r.handle;
121 }
122# ifdef __cpp_impl_three_way_comparison
123 friend inline auto operator<=>(const handle_delegate &l, const handle_delegate &r) noexcept {
124 return l.handle <=> r.handle;
125 }
126# else
127 friend inline bool operator!=(const handle_delegate &l, const handle_delegate &r) noexcept {
128 return l.handle != r.handle;
129 }
130 friend inline bool operator<(const handle_delegate &l, const handle_delegate &r) noexcept {
131 return l.handle < r.handle;
132 }
133 friend inline bool operator<=(const handle_delegate &l, const handle_delegate &r) noexcept {
134 return l.handle <= r.handle;
135 }
136 friend inline bool operator>(const handle_delegate &l, const handle_delegate &r) noexcept {
137 return l.handle > r.handle;
138 }
139 friend inline bool operator>=(const handle_delegate &l, const handle_delegate &r) noexcept {
140 return l.handle >= r.handle;
141 }
142# endif
143 inline operator bool() const noexcept { return !!handle; }
144
145# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
146 template <DerivedPromiseBaseType TPROMISE>
147# else
148 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
149# endif
150 inline handle_delegate &operator=(
151 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &origin_handle) noexcept {
152 handle = origin_handle;
153 if (handle) {
154 promise = &origin_handle.promise();
155 } else {
156 promise = nullptr;
157 }
158
159 return *this;
160 }
161 inline handle_delegate &operator=(std::nullptr_t) noexcept {
162 handle = nullptr;
163 promise = nullptr;
164 return *this;
165 }
166 };
167
168 LIBCOPP_COPP_API promise_caller_manager();
169 LIBCOPP_COPP_API ~promise_caller_manager();
170
171 LIBCOPP_COPP_API void add_caller(handle_delegate handle) noexcept;
172
179 LIBCOPP_COPP_API bool remove_caller(handle_delegate handle) noexcept;
180
181 LIBCOPP_COPP_API size_t resume_callers();
182
183 LIBCOPP_COPP_API bool has_multiple_callers() const noexcept;
184
185 private:
186 // hash for handle_delegate
187 struct LIBCOPP_COPP_API_HEAD_ONLY handle_delegate_hash {
188 inline size_t operator()(const handle_delegate &handle_delegate) const noexcept {
189 return std::hash<void *>()(handle_delegate.handle.address());
190 }
191 };
192
193 // Keep multiple callers in registration order so resume_callers() wakes them in FIFO order, which
194 // matches the stackful backend's next_list behavior and is the least surprising semantics. The
195 // list keeps the wake order and allows O(1) removal by iterator, while the hash index keeps
196 // dedup-on-add and passive removal O(1) as well.
197 struct multi_caller_container {
198 using list_type = std::list<handle_delegate>;
199 using index_type = std::unordered_map<handle_delegate, list_type::iterator, handle_delegate_hash>;
200
201 list_type callers;
202 index_type index;
203
204 inline bool add(const handle_delegate &delegate) {
205 if (index.find(delegate) != index.end()) {
206 return false;
207 }
208 auto back_iter = callers.insert(callers.end(), delegate);
209 index.emplace(delegate, back_iter);
210 return true;
211 }
212
213 inline bool remove(const handle_delegate &delegate) {
214 auto iter = index.find(delegate);
215 if (iter == index.end()) {
216 return false;
217 }
218 callers.erase(iter->second);
219 index.erase(iter);
220 return true;
221 }
222
223 inline void swap(multi_caller_container &other) noexcept {
224 callers.swap(other.callers);
225 index.swap(other.index);
226 }
227
228 inline size_t size() const noexcept { return index.size(); }
229 };
230# if defined(LIBCOPP_MACRO_ENABLE_STD_VARIANT) && LIBCOPP_MACRO_ENABLE_STD_VARIANT
231 std::variant<handle_delegate, multi_caller_container> callers_;
232# else
233 handle_delegate unique_caller_;
234 // Mostly, there is only one caller for a promise, we needn't container to store one handle
235 std::unique_ptr<multi_caller_container> multiple_callers_;
236# endif
237};
238
239class promise_base_type {
240 public:
241 using handle_type = LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<promise_base_type>;
242 using type_erased_handle_type = promise_caller_manager::type_erased_handle_type;
243 using handle_delegate = promise_caller_manager::handle_delegate;
244
245 struct pick_promise_status_awaitable {
246 promise_status data;
247
248 LIBCOPP_COPP_API pick_promise_status_awaitable() noexcept;
249 LIBCOPP_COPP_API pick_promise_status_awaitable(promise_status status) noexcept;
250 LIBCOPP_COPP_API pick_promise_status_awaitable(pick_promise_status_awaitable &&other) noexcept;
251 pick_promise_status_awaitable(const pick_promise_status_awaitable &) = delete;
252 LIBCOPP_COPP_API pick_promise_status_awaitable &operator=(pick_promise_status_awaitable &&) noexcept;
253 pick_promise_status_awaitable &operator=(const pick_promise_status_awaitable &) = delete;
254 LIBCOPP_COPP_API ~pick_promise_status_awaitable();
255
256 LIBCOPP_COPP_API_HEAD_ONLY inline bool await_ready() const noexcept { return true; }
257 LIBCOPP_COPP_API_HEAD_ONLY inline promise_status await_resume() const noexcept { return data; }
258 LIBCOPP_COPP_API_HEAD_ONLY inline void await_suspend(type_erased_handle_type) noexcept {}
259 };
260
261 public:
262 LIBCOPP_COPP_API promise_base_type();
263 LIBCOPP_COPP_API ~promise_base_type();
264
265 LIBCOPP_COPP_API_HEAD_ONLY inline bool set_status(promise_status value, promise_status *expect = nullptr) noexcept {
266 if (nullptr == expect) {
267 status_ = value;
268 return true;
269 }
270 if (status_ == *expect) {
271 status_ = value;
272 return true;
273 } else {
274 *expect = status_;
275 return false;
276 }
277 }
278
279 LIBCOPP_UTIL_FORCEINLINE LIBCOPP_COPP_API_HEAD_ONLY promise_status get_status() const noexcept { return status_; }
280
281 LIBCOPP_COPP_API_HEAD_ONLY inline bool check_flag(promise_flag flag) const noexcept {
282 return 0 != (flags_ & (static_cast<uint32_t>(1) << static_cast<uint8_t>(flag)));
283 }
284
285 LIBCOPP_COPP_API_HEAD_ONLY inline void set_flag(promise_flag flag, bool value) noexcept {
286 uint32_t flag_value = static_cast<uint32_t>(1) << static_cast<uint8_t>(flag);
287 if (value) {
288 flags_ |= flag_value;
289 } else {
290 flags_ &= ~flag_value;
291 }
292 }
293
294 LIBCOPP_COPP_API bool is_waiting() const noexcept;
295 LIBCOPP_COPP_API void set_waiting_handle(std::nullptr_t) noexcept;
296 LIBCOPP_COPP_API void set_waiting_handle(handle_delegate handle);
297# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
298 template <DerivedPromiseBaseType TPROMISE>
299# else
300 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
301# endif
302 LIBCOPP_COPP_API_HEAD_ONLY void set_waiting_handle(
303 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &handle) noexcept {
304 if (nullptr == handle) {
305 set_waiting_handle(nullptr);
306 } else {
307 set_waiting_handle(handle_delegate{handle});
308 }
309 }
310
315# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
316 template <DerivedPromiseBaseType TPROMISE>
317# else
318 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
319# endif
320 LIBCOPP_COPP_API_HEAD_ONLY inline void resume_waiting(
321 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &handle, bool inherit_status) {
322 resume_waiting(handle_delegate{handle}, inherit_status);
323 };
324
325 LIBCOPP_COPP_API void resume_waiting(handle_delegate current_delegate, bool inherit_status);
326
327 // C++20 coroutine
328 struct LIBCOPP_COPP_API_HEAD_ONLY final_awaitable {
329 inline bool await_ready() const noexcept { return false; }
330 inline void await_resume() const noexcept {}
331
332# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
333 template <DerivedPromiseBaseType TPROMISE>
334# else
335 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
336# endif
337 inline void await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> self) noexcept {
338 auto &promise = self.promise();
339 promise.set_flag(promise_flag::kFinalSuspend, true);
340 promise.resume_callers();
341 }
342 };
343 final_awaitable final_suspend() noexcept { return {}; }
344
345 LIBCOPP_COPP_API void add_caller(handle_delegate handle) noexcept;
346# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
347 template <DerivedPromiseBaseType TPROMISE>
348# else
349 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
350# endif
351 LIBCOPP_COPP_API_HEAD_ONLY void add_caller(
352 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &handle) noexcept {
353 add_caller(handle_delegate{handle});
354 }
355
356 LIBCOPP_COPP_API void remove_caller(handle_delegate handle, bool inherit_status) noexcept;
357# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
358 template <DerivedPromiseBaseType TPROMISE>
359# else
360 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
361# endif
362 LIBCOPP_COPP_API_HEAD_ONLY void remove_caller(
363 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &handle, bool inherit_status) noexcept {
364 remove_caller(handle_delegate{handle}, inherit_status);
365 }
366
367 LIBCOPP_UTIL_FORCEINLINE bool has_multiple_callers() const noexcept { return caller_manager_.has_multiple_callers(); }
368
369 LIBCOPP_COPP_API pick_promise_status_awaitable yield_value(pick_promise_status_awaitable &&args) const noexcept;
370 static LIBCOPP_COPP_API_HEAD_ONLY inline pick_promise_status_awaitable pick_current_status() noexcept { return {}; }
371
372 private:
373 LIBCOPP_COPP_API void resume_callers();
374
375 private:
376 // promise_flags
377 uint32_t flags_;
378
379 // promise_status
380 promise_status status_;
381
382 // We must erase type here, because MSVC use is_empty_v<coroutine_handle<...>>, which need to calculate the type size
383 handle_delegate current_waiting_;
384
385 // caller manager
386 promise_caller_manager caller_manager_;
387};
388
389class awaitable_base_type {
390 public:
391 LIBCOPP_COPP_API awaitable_base_type();
392 LIBCOPP_COPP_API ~awaitable_base_type();
393
394 LIBCOPP_COPP_API promise_base_type::handle_delegate get_caller() const noexcept;
395
396 LIBCOPP_COPP_API void set_caller(promise_base_type::handle_delegate caller) noexcept;
397 LIBCOPP_COPP_API void set_caller(std::nullptr_t) noexcept;
398
399# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
400 template <DerivedPromiseBaseType TPROMISE>
401# else
402 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
403# endif
404 LIBCOPP_COPP_API_HEAD_ONLY void set_caller(
405 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE> &handle) noexcept {
406 if (nullptr == handle) {
407 set_caller(nullptr);
408 } else {
409 set_caller(promise_base_type::handle_delegate{handle});
410 }
411 }
412
413 private:
414 promise_base_type::handle_delegate caller_;
415};
416
417template <class TDATA>
418struct LIBCOPP_COPP_API_HEAD_ONLY std_coroutine_default_error_transform;
419
420template <>
421struct LIBCOPP_COPP_API_HEAD_ONLY std_coroutine_default_error_transform<void> {
422 using type = void;
423};
424
425template <class TDATA>
426struct LIBCOPP_COPP_API_HEAD_ONLY std_coroutine_default_error_transform {
427 using type = TDATA;
428 type operator()(promise_status in) const { return type{in}; }
429};
430
431template <class TDATA>
432struct LIBCOPP_COPP_API_HEAD_ONLY std_coroutine_integer_error_transform {
433 using type = TDATA;
434 type operator()(promise_status in) const noexcept {
435 if (in <= promise_status::kCreated) {
436 return static_cast<type>(-1);
437 }
438 return static_cast<type>(-static_cast<int8_t>(in));
439 }
440};
441
442template <class TVALUE>
443struct LIBCOPP_COPP_API_HEAD_ONLY promise_error_transform
444 : public std::conditional<std::is_integral<TVALUE>::value, std_coroutine_integer_error_transform<TVALUE>,
445 std_coroutine_default_error_transform<TVALUE>>::type {
446 using type = TVALUE;
447};
448
449LIBCOPP_COPP_NAMESPACE_END
450
451#endif
atomic wrapper fo integers Licensed under the MIT licenses.
#define LIBCOPP_UTIL_FORCEINLINE
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition coroutine.h:41
constexpr auto size(TCONTAINER &&container) -> decltype(container.size())
Definition span.h:44
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition span.h:54
void swap(intrusive_ptr< T > &lhs, intrusive_ptr< T > &rhs)
STL namespace.