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