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