libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
generator_promise.h
Go to the documentation of this file.
1// Copyright 2023 owent
2
3#pragma once
4
5#include <libcopp/utils/config/libcopp_build_features.h>
6
8
9// clang-format off
10#include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
11// clang-format on
12#include <functional>
13#include <type_traits>
14
15#if defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
16# include <exception>
17#endif
18// clang-format off
19#include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
20// clang-format on
21
26
27#if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
28
29LIBCOPP_COPP_NAMESPACE_BEGIN
30
31enum class generator_vtable_type : uint8_t {
32 kDefault = 0,
33 kLightWeight = 1,
34 kNone = 2,
35};
36
37template <class TCONTEXT, generator_vtable_type VTABLE_TYPE = generator_vtable_type::kDefault>
38class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable;
39
40template <class TCONTEXT, generator_vtable_type VTABLE_TYPE = generator_vtable_type::kDefault>
41struct LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_type_trait;
42
43template <class TVALUE>
44class LIBCOPP_COPP_API_HEAD_ONLY generator_context_base;
45
46template <class TVALUE, class TERROR_TRANSFORM, bool RETURN_VOID>
47class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate;
48
49template <class TVALUE, class TERROR_TRANSFORM>
50class LIBCOPP_COPP_API_HEAD_ONLY generator_context;
51
52template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>,
53 generator_vtable_type VTABLE_TYPE = generator_vtable_type::kDefault>
54class LIBCOPP_COPP_API_HEAD_ONLY generator_future;
55
56template <class TPROMISE, bool RETURN_VOID, generator_vtable_type VTABLE_TYPE>
57class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable;
58
59template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
60using generator_lightweight_future = generator_future<TVALUE, TERROR_TRANSFORM, generator_vtable_type::kLightWeight>;
61
62template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
63using generator_channel_future = generator_future<TVALUE, TERROR_TRANSFORM, generator_vtable_type::kNone>;
64
65template <class TVALUE>
66class LIBCOPP_COPP_API_HEAD_ONLY generator_context_base {
67 public:
68 using value_type = TVALUE;
69 using handle_delegate = promise_caller_manager::handle_delegate;
70
71 private:
72 generator_context_base(const generator_context_base&) = delete;
73 generator_context_base(generator_context_base&&) = delete;
74 generator_context_base& operator=(const generator_context_base&) = delete;
75 generator_context_base& operator=(generator_context_base&&) = delete;
76
77 protected:
78 generator_context_base() = default;
79 ~generator_context_base() { wake(); }
80
81 public:
82 LIBCOPP_UTIL_FORCEINLINE bool is_ready() const noexcept { return data_.is_ready(); }
83
84 LIBCOPP_UTIL_FORCEINLINE bool is_pending() const noexcept { return data_.is_pending(); }
85
86 LIBCOPP_UTIL_FORCEINLINE void reset_value() { data_.reset_data(); }
87
88 LIBCOPP_UTIL_FORCEINLINE void add_caller(handle_delegate handle) noexcept { caller_manager_.add_caller(handle); }
89
90# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
91 template <DerivedPromiseBaseType TPROMISE>
92# else
93 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
94# endif
95 LIBCOPP_UTIL_FORCEINLINE LIBCOPP_COPP_API_HEAD_ONLY void add_caller(
96 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE>& handle) noexcept {
97 add_caller(handle_delegate{handle});
98 }
99
100 LIBCOPP_UTIL_FORCEINLINE void remove_caller(handle_delegate handle) noexcept {
101 caller_manager_.remove_caller(handle);
102 }
103
104# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
105 template <DerivedPromiseBaseType TPROMISE>
106# else
107 template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
108# endif
109 LIBCOPP_UTIL_FORCEINLINE LIBCOPP_COPP_API_HEAD_ONLY void remove_caller(
110 const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE>& handle, bool inherit_status) noexcept {
111 remove_caller(handle_delegate{handle}, inherit_status);
112 }
113
114 LIBCOPP_UTIL_FORCEINLINE bool has_multiple_callers() const noexcept { return caller_manager_.has_multiple_callers(); }
115
116 protected:
117 LIBCOPP_UTIL_FORCEINLINE void wake() { caller_manager_.resume_callers(); }
118
119 protected:
121 // caller manager
122 promise_caller_manager caller_manager_;
123};
124
125template <class TVALUE, class TERROR_TRANSFORM>
126class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate<TVALUE, TERROR_TRANSFORM, true>
127 : public generator_context_base<TVALUE> {
128 public:
129 using base_type = generator_context_base<TVALUE>;
130 using value_type = typename base_type::value_type;
131
132 public:
133 template <class... TARGS>
134 generator_context_delegate(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
135
136 ~generator_context_delegate() { wake(); }
137
138 using base_type::add_caller;
139 using base_type::is_pending;
140 using base_type::is_ready;
141 using base_type::remove_caller;
142 using base_type::reset_value;
143
144 LIBCOPP_UTIL_FORCEINLINE void set_value() {
145 // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
146 // We may enable exception in the future
147# if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
148 std::exception_ptr unhandled_exception;
149 try {
150# endif
151 data_.reset_data(true);
152 wake();
153 // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
154 // We may enable exception in the future
155# if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
156 } catch (...) {
157 unhandled_exception = std::current_exception();
158 }
159 if (unhandled_exception) {
160 std::rethrow_exception(unhandled_exception);
161 }
162# endif
163 }
164
165 private:
166 using base_type::data_;
167 using base_type::wake;
168};
169
170template <class TVALUE, class TERROR_TRANSFORM>
171class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate<TVALUE, TERROR_TRANSFORM, false>
172 : public generator_context_base<TVALUE> {
173 public:
174 using base_type = generator_context_base<TVALUE>;
175 using value_type = typename base_type::value_type;
176
177 public:
178 template <class... TARGS>
179 generator_context_delegate(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
180
181 using base_type::add_caller;
182 using base_type::is_pending;
183 using base_type::is_ready;
184 using base_type::remove_caller;
185 using base_type::reset_value;
186
187 ~generator_context_delegate() {
188 if (is_pending()) {
189 set_value(TERROR_TRANSFORM()(promise_status::kKilled));
190 } else {
191 wake();
192 }
193 }
194
195 LIBCOPP_UTIL_FORCEINLINE const value_type* data() const noexcept {
196 if (!is_ready()) {
197 return nullptr;
198 }
199
200 return data_.data();
201 }
202
204 if (!is_ready()) {
205 return nullptr;
206 }
207
208 return data_.data();
209 }
210
211 template <class U>
212 LIBCOPP_UTIL_FORCEINLINE void set_value(U&& in) {
213 // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
214 // We may enable exception in the future
215# if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
216 std::exception_ptr unhandled_exception;
217 try {
218# endif
219 data_.reset_data(std::forward<U>(in));
220 wake();
221 // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
222 // We may enable exception in the future
223# if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
224 } catch (...) {
225 unhandled_exception = std::current_exception();
226 }
227 if (unhandled_exception) {
228 std::rethrow_exception(unhandled_exception);
229 }
230# endif
231 }
232
233 private:
234 using base_type::data_;
235 using base_type::wake;
236};
237
238template <class TVALUE, class TERROR_TRANSFORM>
239class LIBCOPP_COPP_API_HEAD_ONLY generator_context
240 : public generator_context_delegate<TVALUE, TERROR_TRANSFORM,
241 std::is_void<typename std::decay<TVALUE>::type>::value>,
242 public LIBCOPP_COPP_NAMESPACE_ID::memory::default_enable_shared_from_this<
243 generator_context<TVALUE, TERROR_TRANSFORM>> {
244 public:
245 using base_type =
246 generator_context_delegate<TVALUE, TERROR_TRANSFORM, std::is_void<typename std::decay<TVALUE>::type>::value>;
247 using value_type = typename base_type::value_type;
248 using error_transform = TERROR_TRANSFORM;
249
250 public:
251 template <class... TARGS>
252 generator_context(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
253
254 using base_type::is_pending;
255 using base_type::is_ready;
256 using base_type::reset_value;
257 using base_type::set_value;
258};
259
260template <class TCONTEXT>
261struct LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_type_trait<TCONTEXT, generator_vtable_type::kDefault> {
262 using context_type = TCONTEXT;
263 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
264 using value_type = typename context_type::value_type;
265 using await_suspend_callback_type = std::function<void(context_pointer_type)>;
266 using await_resume_callback_type = std::function<void(const context_type&)>;
267};
268
269template <class TCONTEXT>
270struct LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_type_trait<TCONTEXT, generator_vtable_type::kLightWeight> {
271 using context_type = TCONTEXT;
272 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
273 using value_type = typename context_type::value_type;
274 using await_suspend_callback_type = void (*)(context_pointer_type);
275 using await_resume_callback_type = void (*)(const context_type&);
276};
277
278template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
279class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable {
280 public:
281 using context_type = typename generator_vtable_type_trait<TCONTEXT, VTABLE_TYPE>::context_type;
282 using context_pointer_type = typename generator_vtable_type_trait<TCONTEXT, VTABLE_TYPE>::context_pointer_type;
283 using value_type = typename generator_vtable_type_trait<TCONTEXT, VTABLE_TYPE>::value_type;
284 using await_suspend_callback_type =
285 typename generator_vtable_type_trait<TCONTEXT, VTABLE_TYPE>::await_suspend_callback_type;
286 using await_resume_callback_type =
287 typename generator_vtable_type_trait<TCONTEXT, VTABLE_TYPE>::await_resume_callback_type;
288
289 public:
290 template <class TSUSPEND, class TRESUME>
291 generator_vtable(TSUSPEND&& await_suspend_callback, TRESUME&& await_resume_callback) noexcept(
292 std::is_nothrow_constructible<await_suspend_callback_type, TSUSPEND>::value &&
293 std::is_nothrow_constructible<await_resume_callback_type, TRESUME>::value)
294 : intrusive_ref_counter_(0),
295 await_suspend_callback_(std::forward<TSUSPEND>(await_suspend_callback)),
296 await_resume_callback_(std::forward<TRESUME>(await_resume_callback)) {}
297
298 template <class TSUSPEND>
299 generator_vtable(TSUSPEND&& await_suspend_callback) noexcept(
300 std::is_nothrow_constructible<await_suspend_callback_type, TSUSPEND>::value)
301 : intrusive_ref_counter_(0), await_suspend_callback_(std::forward<TSUSPEND>(await_suspend_callback)) {}
302
303 generator_vtable(const generator_vtable&) = delete;
304 generator_vtable(generator_vtable&&) = delete;
305 generator_vtable& operator=(const generator_vtable&) = delete;
306 generator_vtable& operator=(generator_vtable&&) = delete;
307
308 LIBCOPP_UTIL_FORCEINLINE const await_suspend_callback_type& get_await_suspend_callback() const noexcept {
309 return await_suspend_callback_;
310 }
311 LIBCOPP_UTIL_FORCEINLINE await_suspend_callback_type& get_await_suspend_callback() noexcept {
312 return await_suspend_callback_;
313 }
314 LIBCOPP_UTIL_FORCEINLINE const await_resume_callback_type& get_await_resume_callback() const noexcept {
315 return await_resume_callback_;
316 }
317 LIBCOPP_UTIL_FORCEINLINE await_resume_callback_type& get_await_resume_callback() noexcept {
318 return await_resume_callback_;
319 }
320
321 private:
322 friend void intrusive_ptr_add_ref(generator_vtable* p) {
323 if (nullptr != p) {
324 ++p->intrusive_ref_counter_;
325 }
326 }
327
328 friend void intrusive_ptr_release(generator_vtable* p) {
329 if (nullptr == p) {
330 return;
331 }
332 assert(p->intrusive_ref_counter_ > 0);
333 size_t ref = --p->intrusive_ref_counter_;
334 if (0 == ref) {
335 delete p;
336 }
337 }
338
339 LIBCOPP_UTIL_INTRUSIVE_PTR_ATOMIC_TYPE intrusive_ref_counter_;
340 await_suspend_callback_type await_suspend_callback_;
341 await_resume_callback_type await_resume_callback_;
342};
343
344template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
345class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_delegate;
346
347template <class T>
348struct is_generator_vtable_delegate;
349
350template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
351struct is_generator_vtable_delegate<generator_vtable_delegate<TCONTEXT, VTABLE_TYPE>> : ::std::true_type {};
352
353template <class T>
354struct is_generator_vtable_delegate : ::std::false_type {};
355
356template <class TCONTEXT>
357class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_delegate<TCONTEXT, generator_vtable_type::kDefault> {
358 public:
359 using context_type = TCONTEXT;
360 using vtable_type = generator_vtable<context_type, generator_vtable_type::kDefault>;
361 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
362 using value_type = typename context_type::value_type;
363 using await_suspend_callback_type = typename vtable_type::await_suspend_callback_type;
364 using await_resume_callback_type = typename vtable_type::await_resume_callback_type;
365
366 template <class TSUSPEND, class TRESUME>
367 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(
368 TSUSPEND&& await_suspend_callback,
369 TRESUME&& await_resume_callback) noexcept(std::is_nothrow_constructible<vtable_type, TSUSPEND, TRESUME>::value)
370 : vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback),
371 std::forward<TRESUME>(await_resume_callback))) {}
372
373 template <class TSUSPEND, class = std::enable_if_t<!is_generator_vtable_delegate<TSUSPEND>::value>>
374 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(TSUSPEND&& await_suspend_callback) noexcept(
375 std::is_nothrow_constructible<vtable_type, TSUSPEND>::value)
376 : vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback))) {}
377
378 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate() noexcept : vtable_(nullptr) {}
379
380 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(generator_vtable_delegate&& other) noexcept
381 : vtable_(std::move(other.vtable_)) {}
382 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(const generator_vtable_delegate& other) noexcept
383 : vtable_(other.vtable_) {}
384
385 LIBCOPP_UTIL_FORCEINLINE void trigger_suspend_callback(context_type& context) {
386 if (vtable_ && vtable_->get_await_suspend_callback()) {
387 vtable_->get_await_suspend_callback()(context.shared_from_this());
388 }
389 }
390
391 LIBCOPP_UTIL_FORCEINLINE void trigger_resume_callback(const context_type& context) {
392 if (vtable_ && vtable_->get_await_resume_callback()) {
393 vtable_->get_await_resume_callback()(context);
394 }
395 }
396
397 private:
398 copp::memory::intrusive_ptr<vtable_type> vtable_;
399};
400
401template <class TCONTEXT>
402class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_delegate<TCONTEXT, generator_vtable_type::kLightWeight> {
403 public:
404 using context_type = TCONTEXT;
405 using vtable_type = generator_vtable<context_type, generator_vtable_type::kLightWeight>;
406 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
407 using value_type = typename context_type::value_type;
408 using await_suspend_callback_type = typename vtable_type::await_suspend_callback_type;
409 using await_resume_callback_type = typename vtable_type::await_resume_callback_type;
410
411 template <class TSUSPEND, class TRESUME>
412 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(TSUSPEND&& await_suspend_callback,
413 TRESUME&& await_resume_callback) noexcept
414 : vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback),
415 std::forward<TRESUME>(await_resume_callback))) {}
416
417 template <class TSUSPEND>
418 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(TSUSPEND&& await_suspend_callback) noexcept
419 : vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback), nullptr)) {}
420
421 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate() noexcept : vtable_(nullptr) {}
422
423 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(const generator_vtable_delegate& other) noexcept
424 : vtable_(other.vtable_) {}
425
426 LIBCOPP_UTIL_FORCEINLINE void trigger_suspend_callback(context_type& context) {
427 if (vtable_ && vtable_->get_await_suspend_callback()) {
428 vtable_->get_await_suspend_callback()(context.shared_from_this());
429 }
430 }
431
432 LIBCOPP_UTIL_FORCEINLINE void trigger_resume_callback(const context_type& context) {
433 if (vtable_ && vtable_->get_await_resume_callback()) {
434 vtable_->get_await_resume_callback()(context);
435 }
436 }
437
438 private:
439 copp::memory::intrusive_ptr<vtable_type> vtable_;
440};
441
442template <class TCONTEXT>
443class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable_delegate<TCONTEXT, generator_vtable_type::kNone> {
444 public:
445 using context_type = TCONTEXT;
446 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
447 using value_type = typename context_type::value_type;
448
449 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate() noexcept {}
450 LIBCOPP_UTIL_FORCEINLINE generator_vtable_delegate(const generator_vtable_delegate&) noexcept {}
451
452 LIBCOPP_UTIL_FORCEINLINE void trigger_suspend_callback(context_type& /*context*/) noexcept {}
453
454 LIBCOPP_UTIL_FORCEINLINE void trigger_resume_callback(const context_type& /*context*/) noexcept {}
455};
456
457template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
458class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable_base : public awaitable_base_type {
459 public:
460 using context_type = TCONTEXT;
461 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
462 using value_type = typename context_type::value_type;
463 using vtable_delegate_type = generator_vtable_delegate<context_type, VTABLE_TYPE>;
464
465 public:
466 generator_awaitable_base(context_type* context, const vtable_delegate_type& vtable_delegate)
467 : context_{context}, vtable_delegate_(vtable_delegate) {}
468
469 inline bool await_ready() noexcept {
470 if (nullptr == context_) {
471 return true;
472 }
473
474 return context_->is_ready();
475 }
476
477# if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
478 template <DerivedPromiseBaseType TCPROMISE>
479# else
480 template <class TCPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TCPROMISE>::value>>
481# endif
482 inline bool await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TCPROMISE> caller) noexcept {
483 if (nullptr != context_ && caller.promise().get_status() < promise_status::kDone) {
484 set_caller(caller);
485 context_->add_caller(caller);
486
487 // Allow kill resume to forward error information
488 caller.promise().set_flag(promise_flag::kInternalWaitting, true);
489
490 // Custom event. awaitable object may be deleted after this call
491 vtable_delegate_.trigger_suspend_callback(*context_);
492
493 return true;
494 } else {
495 // Already done and can not suspend again
496 // caller.resume();
497 return false;
498 }
499 }
500
501 protected:
502 promise_status detach() noexcept {
503 promise_status result_status;
504 if LIBCOPP_UTIL_UNLIKELY_CONDITION (nullptr == context_) {
505 result_status = promise_status::kInvalid;
506 } else if (context_->is_ready()) {
507 result_status = promise_status::kDone;
508 } else {
509 result_status = promise_status::kKilled;
510 }
511
512 // caller maybe null if the callable is already ready when co_await
513 auto caller = get_caller();
514
515 if (caller) {
516 if (nullptr != caller.promise) {
517 caller.promise->set_flag(promise_flag::kInternalWaitting, false);
518 }
519 if (nullptr != context_) {
520 if (!context_->is_ready() && nullptr != caller.promise) {
521 result_status = caller.promise->get_status();
522 }
523
524 context_->remove_caller(caller);
525 set_caller(nullptr);
526
527 // Custom event
528 vtable_delegate_.trigger_resume_callback(*context_);
529 } else {
530 set_caller(nullptr);
531 }
532 }
533
534 return result_status;
535 }
536
543 inline context_type* get_context() noexcept { return context_; }
544
545 private:
546 context_type* context_;
547 vtable_delegate_type vtable_delegate_;
548};
549
550template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
551class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable<TCONTEXT, true, VTABLE_TYPE>
552 : public generator_awaitable_base<TCONTEXT, VTABLE_TYPE> {
553 public:
554 using base_type = generator_awaitable_base<TCONTEXT, VTABLE_TYPE>;
555 using value_type = typename base_type::value_type;
556 using context_type = typename base_type::context_type;
557 using context_pointer_type = typename base_type::context_pointer_type;
558 using vtable_delegate_type = typename base_type::vtable_delegate_type;
559 using error_transform = typename context_type::error_transform;
560
561 public:
562 using base_type::await_ready;
563 using base_type::await_suspend;
564 using base_type::get_caller;
565 using base_type::set_caller;
566 generator_awaitable(context_type* context, const vtable_delegate_type& vtable_delegate)
567 : base_type(context, vtable_delegate) {}
568
569 inline void await_resume() { detach(); }
570
571 private:
572 using base_type::detach;
573 using base_type::get_context;
574};
575
576template <class TCONTEXT, generator_vtable_type VTABLE_TYPE>
577class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable<TCONTEXT, false, VTABLE_TYPE>
578 : public generator_awaitable_base<TCONTEXT, VTABLE_TYPE> {
579 public:
580 using base_type = generator_awaitable_base<TCONTEXT, VTABLE_TYPE>;
581 using value_type = typename base_type::value_type;
582 using context_type = typename base_type::context_type;
583 using context_pointer_type = typename base_type::context_pointer_type;
584 using vtable_delegate_type = typename base_type::vtable_delegate_type;
585 using error_transform = typename context_type::error_transform;
586
587 public:
588 using base_type::await_ready;
589 using base_type::await_suspend;
590 using base_type::get_caller;
591 using base_type::set_caller;
592 generator_awaitable(context_type* context, const vtable_delegate_type& vtable_delegate)
593 : base_type(context, vtable_delegate) {}
594
595 inline value_type await_resume() {
596 bool has_multiple_callers;
597 if LIBCOPP_UTIL_LIKELY_CONDITION (nullptr != get_context()) {
598 has_multiple_callers = get_context()->has_multiple_callers();
599 } else {
600 has_multiple_callers = false;
601 }
602 promise_status result_status = detach();
603
604 if (promise_status::kDone != result_status) {
605 return error_transform()(result_status);
606 }
607
608 if LIBCOPP_UTIL_LIKELY_CONDITION (nullptr != get_context()) {
609 if (has_multiple_callers) {
610 return *get_context()->data();
611 } else {
612 return multiple_callers_constructor<value_type>::return_value(*get_context()->data());
613 }
614 } else {
615 return error_transform()(promise_status::kInvalid);
616 }
617 }
618
619 private:
620 using base_type::detach;
621 using base_type::get_context;
622};
623
624template <class T>
625struct is_generator_future;
626
627template <class TVALUE, class TERROR_TRANSFORM, generator_vtable_type VTABLE_TYPE>
628struct is_generator_future<generator_future<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>> : ::std::true_type {};
629
630template <class T>
631struct is_generator_future : ::std::false_type {};
632
633template <class TVALUE, class TERROR_TRANSFORM, generator_vtable_type VTABLE_TYPE>
634class LIBCOPP_COPP_API_HEAD_ONLY generator_future {
635 public:
636 using value_type = TVALUE;
637 using error_transform = TERROR_TRANSFORM;
638 using self_type = generator_future<value_type, error_transform, VTABLE_TYPE>;
639 using context_type = generator_context<value_type, error_transform>;
640 using context_pointer_type = LIBCOPP_COPP_NAMESPACE_ID::memory::default_strong_rc_ptr<context_type>;
641 using awaitable_type =
642 generator_awaitable<context_type, std::is_void<typename std::decay<value_type>::type>::value, VTABLE_TYPE>;
643 using vtable_delegate_type = generator_vtable_delegate<context_type, VTABLE_TYPE>;
644
645 public:
646 template <class TSUSPEND, class TRESUME,
647 class = nostd::enable_if_t<!is_generator_vtable_delegate<nostd::decay_t<TSUSPEND>>::value>>
648 inline generator_future(TSUSPEND&& await_suspend_callback,
649 TRESUME&& await_resume_callback) noexcept(std::is_nothrow_constructible<context_type>::value)
650 : context_(LIBCOPP_COPP_NAMESPACE_ID::memory::default_make_strong<context_type>()),
651 vtable_delegate_(std::forward<TSUSPEND>(await_suspend_callback), std::forward<TRESUME>(await_resume_callback)) {
652 }
653
654 template <class TSUSPEND, class = nostd::enable_if_t<!is_generator_vtable_delegate<nostd::decay_t<TSUSPEND>>::value &&
655 !is_generator_future<nostd::decay_t<TSUSPEND>>::value>>
656 inline generator_future(TSUSPEND&& await_suspend_callback) noexcept(
657 std::is_nothrow_constructible<context_type>::value)
658 : context_(LIBCOPP_COPP_NAMESPACE_ID::memory::default_make_strong<context_type>()),
659 vtable_delegate_(std::forward<TSUSPEND>(await_suspend_callback)) {}
660
661 inline generator_future() noexcept(std::is_nothrow_constructible<context_type>::value)
662 : context_(LIBCOPP_COPP_NAMESPACE_ID::memory::default_make_strong<context_type>()) {}
663
664 inline generator_future(generator_future&&) = default;
665 inline generator_future(const generator_future&) = default;
666 inline generator_future& operator=(generator_future&&) = default;
667 inline generator_future& operator=(const generator_future&) = default;
668
669 ~generator_future() {
670 if (context_) {
671 context_.reset();
672 }
673 }
674
675 inline awaitable_type operator co_await() {
676 // generator may be destroyed before awaitable_type, but context will not
677 return awaitable_type{context_.get(), vtable_delegate_};
678 }
679
680 inline bool is_ready() const noexcept {
681 if (!context_) {
682 return false;
683 }
684
685 return context_->is_ready();
686 }
687
688 inline bool is_pending() const noexcept {
689 if (!context_) {
690 return false;
691 }
692
693 return context_->is_pending();
694 }
695
696 inline promise_status get_status() const noexcept {
697 if (!context_) {
698 return promise_status::kInvalid;
699 }
700
701 if (context_->is_ready()) {
702 return promise_status::kDone;
703 }
704
705 return promise_status::kRunning;
706 }
707
708 LIBCOPP_UTIL_FORCEINLINE const context_pointer_type& get_context() const noexcept { return context_; }
709
710 LIBCOPP_UTIL_FORCEINLINE context_pointer_type& get_context() noexcept { return context_; }
711
712 private:
713 template <class TFUTURE>
714 friend class LIBCOPP_COPP_API_HEAD_ONLY some_delegate;
715
716 template <class TFUTURE, class, generator_vtable_type>
717 friend struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_generator_action;
718
719 context_pointer_type context_;
720 vtable_delegate_type vtable_delegate_;
721};
722
723// some
724template <class TVALUE, class TERROR_TRANSFORM, generator_vtable_type VTABLE_TYPE>
725struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_generator_action {
726 using future_type = generator_future<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>;
727 using context_type = some_delegate_context<future_type>;
728
729 inline static void suspend_future(const promise_caller_manager::handle_delegate& caller, future_type& generator) {
730 generator.get_context()->add_caller(caller);
731
732 // Custom event. awaitable object may be deleted after this call
733 generator.vtable_delegate_.trigger_suspend_callback(*generator.get_context());
734 }
735
736 inline static void resume_future(const promise_caller_manager::handle_delegate& caller, future_type& generator) {
737 generator.get_context()->remove_caller(caller);
738
739 // Custom event
740 generator.vtable_delegate_.trigger_resume_callback(*generator.get_context());
741 }
742
743 inline static bool is_pending(future_type& future_object) noexcept { return future_object.is_pending(); }
744};
745
746template <class TVALUE, class TERROR_TRANSFORM, generator_vtable_type VTABLE_TYPE>
747class LIBCOPP_COPP_API_HEAD_ONLY some_delegate<generator_future<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>>
748 : public some_delegate_base<generator_future<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>,
749 some_delegate_generator_action<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>> {
750 public:
751 using base_type = some_delegate_base<generator_future<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>,
752 some_delegate_generator_action<TVALUE, TERROR_TRANSFORM, VTABLE_TYPE>>;
753 using future_type = typename base_type::future_type;
754 using value_type = typename base_type::value_type;
755 using ready_output_type = typename base_type::ready_output_type;
756 using context_type = typename base_type::context_type;
757
758 using base_type::run;
759};
760
761template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
762using generator_channel_receiver = generator_channel_future<TVALUE, TERROR_TRANSFORM>;
763
764template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
765using generator_channel_sender = typename generator_channel_receiver<TVALUE, TERROR_TRANSFORM>::context_pointer_type;
766
767template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
768inline std::pair<generator_channel_receiver<TVALUE, TERROR_TRANSFORM>,
769 generator_channel_sender<TVALUE, TERROR_TRANSFORM>>
770make_channel() {
771 generator_channel_receiver<TVALUE, TERROR_TRANSFORM> future;
772 generator_channel_sender<TVALUE, TERROR_TRANSFORM> context = future.get_context();
773 return std::make_pair(std::move(future), std::move(context));
774}
775
776LIBCOPP_COPP_NAMESPACE_END
777
778#endif
#define LIBCOPP_UTIL_FORCEINLINE
#define LIBCOPP_UTIL_LIKELY_CONDITION(__C)
#define LIBCOPP_UTIL_UNLIKELY_CONDITION(__C)
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition coroutine.h:41
#define LIBCOPP_UTIL_INTRUSIVE_PTR_ATOMIC_TYPE
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition span.h:54
LIBCOPP_UTIL_FORCEINLINE default_strong_rc_ptr< T > default_make_strong(ArgsT &&... args)
STL namespace.
std::shared_ptr< cli::cmd_option_value > value_type
Definition cmd_option.h:50