libcopp  2.2.0
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 
25 #include "libcopp/future/future.h"
26 
27 #if defined(LIBCOPP_MACRO_ENABLE_STD_COROUTINE) && LIBCOPP_MACRO_ENABLE_STD_COROUTINE
28 
29 LIBCOPP_COPP_NAMESPACE_BEGIN
30 
31 template <class TVALUE>
32 class LIBCOPP_COPP_API_HEAD_ONLY generator_context_base;
33 
34 template <class TVALUE, class TERROR_TRANSFORM, bool RETURN_VOID>
35 class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate;
36 
37 template <class TVALUE, class TERROR_TRANSFORM>
38 class LIBCOPP_COPP_API_HEAD_ONLY generator_context;
39 
40 template <class TVALUE, class TERROR_TRANSFORM>
41 class LIBCOPP_COPP_API_HEAD_ONLY generator_future;
42 
43 template <class TPROMISE, bool RETURN_VOID>
44 class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable;
45 
46 template <class TVALUE>
47 class LIBCOPP_COPP_API_HEAD_ONLY generator_context_base {
48  public:
49  using value_type = TVALUE;
50  using handle_delegate = promise_caller_manager::handle_delegate;
51 
52  private:
53  generator_context_base(const generator_context_base&) = delete;
54  generator_context_base(generator_context_base&&) = delete;
55  generator_context_base& operator=(const generator_context_base&) = delete;
56  generator_context_base& operator=(generator_context_base&&) = delete;
57 
58  protected:
59  generator_context_base() = default;
60  ~generator_context_base() { wake(); }
61 
62  public:
63  UTIL_FORCEINLINE bool is_ready() const noexcept { return data_.is_ready(); }
64 
65  UTIL_FORCEINLINE bool is_pending() const noexcept { return data_.is_pending(); }
66 
67  UTIL_FORCEINLINE void reset_value() { data_.reset_data(); }
68 
69  UTIL_FORCEINLINE void add_caller(handle_delegate handle) noexcept { caller_manager_.add_caller(handle); }
70 
71 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
72  template <DerivedPromiseBaseType TPROMISE>
73 # else
74  template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
75 # endif
76  UTIL_FORCEINLINE LIBCOPP_COPP_API_HEAD_ONLY void add_caller(
77  const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE>& handle) noexcept {
78  add_caller(handle_delegate{handle});
79  }
80 
81  UTIL_FORCEINLINE void remove_caller(handle_delegate handle) noexcept { caller_manager_.remove_caller(handle); }
82 
83 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
84  template <DerivedPromiseBaseType TPROMISE>
85 # else
86  template <class TPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TPROMISE>::value>>
87 # endif
88  UTIL_FORCEINLINE LIBCOPP_COPP_API_HEAD_ONLY void remove_caller(
89  const LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TPROMISE>& handle, bool inherit_status) noexcept {
90  remove_caller(handle_delegate{handle}, inherit_status);
91  }
92 
93  UTIL_FORCEINLINE bool has_multiple_callers() const noexcept { return caller_manager_.has_multiple_callers(); }
94 
95  protected:
96  UTIL_FORCEINLINE void wake() { caller_manager_.resume_callers(); }
97 
98  protected:
100  // caller manager
101  promise_caller_manager caller_manager_;
102 };
103 
104 template <class TVALUE, class TERROR_TRANSFORM>
105 class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate<TVALUE, TERROR_TRANSFORM, true>
106  : public generator_context_base<TVALUE> {
107  public:
108  using base_type = generator_context_base<TVALUE>;
109  using value_type = typename base_type::value_type;
110 
111  public:
112  template <class... TARGS>
113  generator_context_delegate(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
114 
115  ~generator_context_delegate() { wake(); }
116 
117  using base_type::add_caller;
118  using base_type::is_pending;
119  using base_type::is_ready;
120  using base_type::remove_caller;
121  using base_type::reset_value;
122 
123  UTIL_FORCEINLINE void set_value() {
124  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
125  // We may enable exception in the future
126 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
127  std::exception_ptr unhandled_exception;
128  try {
129 # endif
130  data_.reset_data(true);
131  wake();
132  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
133  // We may enable exception in the future
134 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
135  } catch (...) {
136  unhandled_exception = std::current_exception();
137  }
138  if (unhandled_exception) {
139  std::rethrow_exception(unhandled_exception);
140  }
141 # endif
142  }
143 
144  private:
145  using base_type::data_;
146  using base_type::wake;
147 };
148 
149 template <class TVALUE, class TERROR_TRANSFORM>
150 class LIBCOPP_COPP_API_HEAD_ONLY generator_context_delegate<TVALUE, TERROR_TRANSFORM, false>
151  : public generator_context_base<TVALUE> {
152  public:
153  using base_type = generator_context_base<TVALUE>;
154  using value_type = typename base_type::value_type;
155 
156  public:
157  template <class... TARGS>
158  generator_context_delegate(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
159 
160  using base_type::add_caller;
161  using base_type::is_pending;
162  using base_type::is_ready;
163  using base_type::remove_caller;
164  using base_type::reset_value;
165 
166  ~generator_context_delegate() {
167  if (is_pending()) {
168  set_value(TERROR_TRANSFORM()(promise_status::kKilled));
169  } else {
170  wake();
171  }
172  }
173 
174  UTIL_FORCEINLINE const value_type* data() const noexcept {
175  if (!is_ready()) {
176  return nullptr;
177  }
178 
179  return data_.data();
180  }
181 
182  UTIL_FORCEINLINE value_type* data() noexcept {
183  if (!is_ready()) {
184  return nullptr;
185  }
186 
187  return data_.data();
188  }
189 
190  template <class U>
191  UTIL_FORCEINLINE void set_value(U&& in) {
192  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
193  // We may enable exception in the future
194 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
195  std::exception_ptr unhandled_exception;
196  try {
197 # endif
198  data_.reset_data(std::forward<U>(in));
199  wake();
200  // rethrow a exception in c++20 coroutine will crash when using MSVC now(VS2022)
201  // We may enable exception in the future
202 # if 0 && defined(LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR) && LIBCOPP_MACRO_ENABLE_STD_EXCEPTION_PTR
203  } catch (...) {
204  unhandled_exception = std::current_exception();
205  }
206  if (unhandled_exception) {
207  std::rethrow_exception(unhandled_exception);
208  }
209 # endif
210  }
211 
212  private:
213  using base_type::data_;
214  using base_type::wake;
215 };
216 
217 template <class TVALUE, class TERROR_TRANSFORM>
218 class LIBCOPP_COPP_API_HEAD_ONLY generator_context
219  : public generator_context_delegate<TVALUE, TERROR_TRANSFORM,
220  std::is_void<typename std::decay<TVALUE>::type>::value>,
221  public std::enable_shared_from_this<generator_context<TVALUE, TERROR_TRANSFORM>> {
222  public:
223  using base_type =
224  generator_context_delegate<TVALUE, TERROR_TRANSFORM, std::is_void<typename std::decay<TVALUE>::type>::value>;
225  using value_type = typename base_type::value_type;
226  using error_transform = TERROR_TRANSFORM;
227 
228  public:
229  template <class... TARGS>
230  generator_context(TARGS&&... args) : base_type(std::forward<TARGS>(args)...) {}
231 
232  using base_type::is_pending;
233  using base_type::is_ready;
234  using base_type::reset_value;
235  using base_type::set_value;
236 };
237 
238 template <class TCONTEXT>
239 class LIBCOPP_COPP_API_HEAD_ONLY generator_vtable {
240  public:
241  using context_type = TCONTEXT;
242  using context_pointer_type = std::shared_ptr<context_type>;
243  using value_type = typename context_type::value_type;
244  using await_suspend_callback_type = std::function<void(context_pointer_type)>;
245  using await_resume_callback_type = std::function<void(const context_type&)>;
246 
247  public:
248  template <class TSUSPEND, class TRESUME>
249  generator_vtable(TSUSPEND&& await_suspend_callback, TRESUME&& await_resume_callback)
250  : intrusive_ref_counter_(0),
251  await_suspend_callback_(std::forward<TSUSPEND>(await_suspend_callback)),
252  await_resume_callback_(std::forward<TRESUME>(await_resume_callback)) {}
253 
254  template <class TSUSPEND>
255  generator_vtable(TSUSPEND&& await_suspend_callback)
256  : intrusive_ref_counter_(0), await_suspend_callback_(std::forward<TSUSPEND>(await_suspend_callback)) {}
257 
258  generator_vtable(const generator_vtable&) = delete;
259  generator_vtable(generator_vtable&&) = delete;
260  generator_vtable& operator=(const generator_vtable&) = delete;
261  generator_vtable& operator=(generator_vtable&&) = delete;
262 
263  UTIL_FORCEINLINE const await_suspend_callback_type& get_await_suspend_callback() const noexcept {
264  return await_suspend_callback_;
265  }
266  UTIL_FORCEINLINE await_suspend_callback_type& get_await_suspend_callback() noexcept {
267  return await_suspend_callback_;
268  }
269  UTIL_FORCEINLINE const await_resume_callback_type& get_await_resume_callback() const noexcept {
270  return await_resume_callback_;
271  }
272  UTIL_FORCEINLINE await_resume_callback_type& get_await_resume_callback() noexcept { return await_resume_callback_; }
273 
274  private:
275  friend void intrusive_ptr_add_ref(generator_vtable* p) {
276  if (nullptr != p) {
277  ++p->intrusive_ref_counter_;
278  }
279  }
280 
281  friend void intrusive_ptr_release(generator_vtable* p) {
282  if (nullptr == p) {
283  return;
284  }
285  assert(p->intrusive_ref_counter_ > 0);
286  size_t ref = --p->intrusive_ref_counter_;
287  if (0 == ref) {
288  delete p;
289  }
290  }
291 
292  size_t intrusive_ref_counter_;
293  await_suspend_callback_type await_suspend_callback_;
294  await_resume_callback_type await_resume_callback_;
295 };
296 
297 template <class TCONTEXT>
298 class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable_base : public awaitable_base_type {
299  public:
300  using context_type = TCONTEXT;
301  using context_pointer_type = std::shared_ptr<context_type>;
302  using value_type = typename context_type::value_type;
303  using vtable_type = generator_vtable<context_type>;
304  using await_suspend_callback_type = typename vtable_type::await_suspend_callback_type;
305  using await_resume_callback_type = typename vtable_type::await_resume_callback_type;
306 
307  public:
308  generator_awaitable_base(context_type* context, const copp::util::intrusive_ptr<vtable_type>& vtable)
309  : context_{context}, vtable_(vtable) {}
310 
311  inline bool await_ready() noexcept {
312  if (nullptr == context_) {
313  return true;
314  }
315 
316  return context_->is_ready();
317  }
318 
319 # if defined(LIBCOPP_MACRO_ENABLE_CONCEPTS) && LIBCOPP_MACRO_ENABLE_CONCEPTS
320  template <DerivedPromiseBaseType TCPROMISE>
321 # else
322  template <class TCPROMISE, typename = std::enable_if_t<std::is_base_of<promise_base_type, TCPROMISE>::value>>
323 # endif
324  inline bool await_suspend(LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE coroutine_handle<TCPROMISE> caller) noexcept {
325  if (nullptr != context_ && caller.promise().get_status() < promise_status::kDone) {
326  set_caller(caller);
327  context_->add_caller(caller);
328 
329  // Allow kill resume to forward error information
330  caller.promise().set_flag(promise_flag::kInternalWaitting, true);
331 
332  // Custom event. awaitable object may be deleted after this call
333  if (vtable_ && vtable_->get_await_suspend_callback()) {
334  vtable_->get_await_suspend_callback()(context_->shared_from_this());
335  }
336 
337  return true;
338  } else {
339  // Already done and can not suspend again
340  // caller.resume();
341  return false;
342  }
343  }
344 
345  protected:
346  promise_status detach() noexcept {
347  promise_status result_status;
348  COPP_UNLIKELY_IF (nullptr == context_) {
349  result_status = promise_status::kInvalid;
350  } else if (context_->is_ready()) {
351  result_status = promise_status::kDone;
352  } else {
353  result_status = promise_status::kKilled;
354  }
355 
356  // caller maybe null if the callable is already ready when co_await
357  auto caller = get_caller();
358 
359  if (caller) {
360  if (nullptr != caller.promise) {
361  caller.promise->set_flag(promise_flag::kInternalWaitting, false);
362  }
363  if (nullptr != context_) {
364  if (!context_->is_ready() && nullptr != caller.promise) {
365  result_status = caller.promise->get_status();
366  }
367 
368  context_->remove_caller(caller);
369  set_caller(nullptr);
370 
371  // Custom event
372  if (vtable_ && vtable_->get_await_resume_callback()) {
373  vtable_->get_await_resume_callback()(*context_);
374  }
375  } else {
376  set_caller(nullptr);
377  }
378  }
379 
380  return result_status;
381  }
382 
389  inline context_type* get_context() noexcept { return context_; }
390 
391  private:
392  context_type* context_;
393  copp::util::intrusive_ptr<vtable_type> vtable_;
394 };
395 
396 template <class TCONTEXT>
397 class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable<TCONTEXT, true> : public generator_awaitable_base<TCONTEXT> {
398  public:
399  using base_type = generator_awaitable_base<TCONTEXT>;
400  using value_type = typename base_type::value_type;
401  using context_type = typename base_type::context_type;
402  using context_pointer_type = typename base_type::context_pointer_type;
403  using vtable_type = typename base_type::vtable_type;
404  using await_suspend_callback_type = typename base_type::await_suspend_callback_type;
405  using await_resume_callback_type = typename base_type::await_resume_callback_type;
406  using error_transform = typename context_type::error_transform;
407 
408  public:
409  using base_type::await_ready;
410  using base_type::await_suspend;
411  using base_type::get_caller;
412  using base_type::set_caller;
413  generator_awaitable(context_type* context, const copp::util::intrusive_ptr<vtable_type>& vtable)
414  : base_type(context, vtable) {}
415 
416  inline void await_resume() { detach(); }
417 
418  private:
419  using base_type::detach;
420  using base_type::get_context;
421 };
422 
423 template <class TCONTEXT>
424 class LIBCOPP_COPP_API_HEAD_ONLY generator_awaitable<TCONTEXT, false> : public generator_awaitable_base<TCONTEXT> {
425  public:
426  using base_type = generator_awaitable_base<TCONTEXT>;
427  using value_type = typename base_type::value_type;
428  using context_type = typename base_type::context_type;
429  using context_pointer_type = typename base_type::context_pointer_type;
430  using vtable_type = typename base_type::vtable_type;
431  using await_suspend_callback_type = typename base_type::await_suspend_callback_type;
432  using await_resume_callback_type = typename base_type::await_resume_callback_type;
433  using error_transform = typename context_type::error_transform;
434 
435  public:
436  using base_type::await_ready;
437  using base_type::await_suspend;
438  using base_type::get_caller;
439  using base_type::set_caller;
440  generator_awaitable(context_type* context, const copp::util::intrusive_ptr<vtable_type>& vtable)
441  : base_type(context, vtable) {}
442 
443  inline value_type await_resume() {
444  bool has_multiple_callers;
445  COPP_LIKELY_IF (nullptr != get_context()) {
446  has_multiple_callers = get_context()->has_multiple_callers();
447  } else {
448  has_multiple_callers = false;
449  }
450  promise_status result_status = detach();
451 
452  if (promise_status::kDone != result_status) {
453  return error_transform()(result_status);
454  }
455 
456  COPP_LIKELY_IF (nullptr != get_context()) {
457  if (has_multiple_callers) {
458  return *get_context()->data();
459  } else {
460  return multiple_callers_constructor<value_type>::return_value(*get_context()->data());
461  }
462  } else {
463  return error_transform()(promise_status::kInvalid);
464  }
465  }
466 
467  private:
468  using base_type::detach;
469  using base_type::get_context;
470 };
471 
472 template <class TVALUE, class TERROR_TRANSFORM = promise_error_transform<TVALUE>>
473 class LIBCOPP_COPP_API_HEAD_ONLY generator_future {
474  public:
475  using value_type = TVALUE;
476  using error_transform = TERROR_TRANSFORM;
477  using self_type = generator_future<value_type, error_transform>;
478  using context_type = generator_context<value_type, error_transform>;
479  using context_pointer_type = std::shared_ptr<context_type>;
480  using awaitable_type = generator_awaitable<context_type, std::is_void<typename std::decay<value_type>::type>::value>;
481  using vtable_type = typename awaitable_type::vtable_type;
482  using await_suspend_callback_type = typename awaitable_type::await_suspend_callback_type;
483  using await_resume_callback_type = typename awaitable_type::await_resume_callback_type;
484 
485  public:
486  template <class TSUSPEND, class TRESUME>
487  generator_future(TSUSPEND&& await_suspend_callback, TRESUME&& await_resume_callback)
488  : context_(std::make_shared<context_type>()),
489  vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback),
490  std::forward<TRESUME>(await_resume_callback))) {}
491 
492  template <class TSUSPEND>
493  generator_future(TSUSPEND&& await_suspend_callback)
494  : context_(std::make_shared<context_type>()),
495  vtable_(new vtable_type(std::forward<TSUSPEND>(await_suspend_callback))) {}
496 
497  generator_future(generator_future&&) = default;
498  generator_future(const generator_future&) = default;
499  generator_future& operator=(generator_future&&) = default;
500  generator_future& operator=(const generator_future&) = default;
501 
502  ~generator_future() {
503  if (context_) {
504  context_.reset();
505  }
506  }
507 
508  awaitable_type operator co_await() {
509  // generator may be destroyed before awaitable_type, but context will not
510  return awaitable_type{context_.get(), vtable_};
511  }
512 
513  inline bool is_ready() const noexcept {
514  if (!context_) {
515  return false;
516  }
517 
518  return context_->is_ready();
519  }
520 
521  inline bool is_pending() const noexcept {
522  if (!context_) {
523  return false;
524  }
525 
526  return context_->is_pending();
527  }
528 
529  inline promise_status get_status() const noexcept {
530  if (!context_) {
531  return promise_status::kInvalid;
532  }
533 
534  if (context_->is_ready()) {
535  return promise_status::kDone;
536  }
537 
538  return promise_status::kRunning;
539  }
540 
541  UTIL_FORCEINLINE const std::shared_ptr<context_type>& get_context() const noexcept { return context_; }
542 
543  UTIL_FORCEINLINE std::shared_ptr<context_type>& get_context() noexcept { return context_; }
544 
545  private:
546  template <class TFUTURE>
547  friend class LIBCOPP_COPP_API_HEAD_ONLY some_delegate;
548 
549  template <class TFUTURE, class>
550  friend struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_generator_action;
551 
552  std::shared_ptr<context_type> context_;
553  copp::util::intrusive_ptr<vtable_type> vtable_;
554 };
555 
556 // some
557 template <class TVALUE, class TERROR_TRANSFORM>
558 struct LIBCOPP_COPP_API_HEAD_ONLY some_delegate_generator_action {
559  using future_type = generator_future<TVALUE, TERROR_TRANSFORM>;
560  using context_type = some_delegate_context<future_type>;
561 
562  inline static void suspend_future(const promise_caller_manager::handle_delegate& caller, future_type& generator) {
563  generator.get_context()->add_caller(caller);
564 
565  // Custom event. awaitable object may be deleted after this call
566  if (generator.vtable_ && generator.vtable_->get_await_suspend_callback()) {
567  generator.vtable_->get_await_suspend_callback()(generator.get_context());
568  }
569  }
570 
571  inline static void resume_future(const promise_caller_manager::handle_delegate& caller, future_type& generator) {
572  generator.get_context()->remove_caller(caller);
573 
574  // Custom event
575  if (generator.vtable_ && generator.vtable_->get_await_resume_callback()) {
576  generator.vtable_->get_await_resume_callback()(*generator.get_context());
577  }
578  }
579 
580  inline static bool is_pending(future_type& future_object) noexcept { return future_object.is_pending(); }
581 };
582 
583 template <class TVALUE, class TERROR_TRANSFORM>
584 class LIBCOPP_COPP_API_HEAD_ONLY some_delegate<generator_future<TVALUE, TERROR_TRANSFORM>>
585  : public some_delegate_base<generator_future<TVALUE, TERROR_TRANSFORM>,
586  some_delegate_generator_action<TVALUE, TERROR_TRANSFORM>> {
587  public:
588  using base_type = some_delegate_base<generator_future<TVALUE, TERROR_TRANSFORM>,
589  some_delegate_generator_action<TVALUE, TERROR_TRANSFORM>>;
590  using future_type = typename base_type::future_type;
591  using value_type = typename base_type::value_type;
592  using ready_output_type = typename base_type::ready_output_type;
593  using context_type = typename base_type::context_type;
594 
595  using base_type::run;
596 };
597 
598 LIBCOPP_COPP_NAMESPACE_END
599 
600 #endif
#define UTIL_FORCEINLINE
#define LIBCOPP_MACRO_STD_COROUTINE_NAMESPACE
Definition: coroutine.h:41
#define COPP_UNLIKELY_IF(...)
Definition: features.h:117
#define COPP_LIKELY_IF(...)
Definition: features.h:102
constexpr auto data(TCONTAINER &&container) -> decltype(container.data())
Definition: span.h:54
std::shared_ptr< cli::cmd_option_value > value_type
Definition: cmd_option.h:50