1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef _LIBCPP___COROUTINE_COROUTINE_HANDLE_H
10 #define _LIBCPP___COROUTINE_COROUTINE_HANDLE_H
11 
12 #include <__assert>
13 #include <__config>
14 #include <__functional/hash.h>
15 #include <__memory/addressof.h>
16 #include <__type_traits/remove_cv.h>
17 #include <compare>
18 #include <cstddef>
19 
20 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
21 #  pragma GCC system_header
22 #endif
23 
24 #if _LIBCPP_STD_VER >= 20
25 
26 _LIBCPP_BEGIN_NAMESPACE_STD
27 
28 // [coroutine.handle]
29 template <class _Promise = void>
30 struct _LIBCPP_TEMPLATE_VIS coroutine_handle;
31 
32 template <>
33 struct _LIBCPP_TEMPLATE_VIS coroutine_handle<void> {
34 public:
35   // [coroutine.handle.con], construct/reset
36   constexpr coroutine_handle() noexcept = default;
37 
38   _LIBCPP_HIDE_FROM_ABI constexpr coroutine_handle(nullptr_t) noexcept {}
39 
40   _LIBCPP_HIDE_FROM_ABI coroutine_handle& operator=(nullptr_t) noexcept {
41     __handle_ = nullptr;
42     return *this;
43   }
44 
45   // [coroutine.handle.export.import], export/import
46   _LIBCPP_HIDE_FROM_ABI constexpr void* address() const noexcept { return __handle_; }
47 
48   _LIBCPP_HIDE_FROM_ABI static constexpr coroutine_handle from_address(void* __addr) noexcept {
49     coroutine_handle __tmp;
50     __tmp.__handle_ = __addr;
51     return __tmp;
52   }
53 
54   // [coroutine.handle.observers], observers
55   _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __handle_ != nullptr; }
56 
57   _LIBCPP_HIDE_FROM_ABI bool done() const {
58     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "done() can be called only on suspended coroutines");
59     return __builtin_coro_done(__handle_);
60   }
61 
62   // [coroutine.handle.resumption], resumption
63   _LIBCPP_HIDE_FROM_ABI void operator()() const { resume(); }
64 
65   _LIBCPP_HIDE_FROM_ABI void resume() const {
66     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "resume() can be called only on suspended coroutines");
67     _LIBCPP_ASSERT_UNCATEGORIZED(!done(), "resume() has undefined behavior when the coroutine is done");
68     __builtin_coro_resume(__handle_);
69   }
70 
71   _LIBCPP_HIDE_FROM_ABI void destroy() const {
72     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "destroy() can be called only on suspended coroutines");
73     __builtin_coro_destroy(__handle_);
74   }
75 
76 private:
77   _LIBCPP_HIDE_FROM_ABI bool __is_suspended() const {
78     // FIXME actually implement a check for if the coro is suspended.
79     return __handle_ != nullptr;
80   }
81 
82   void* __handle_ = nullptr;
83 };
84 
85 // [coroutine.handle.compare]
86 inline _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {
87   return __x.address() == __y.address();
88 }
89 inline _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering
90 operator<=>(coroutine_handle<> __x, coroutine_handle<> __y) noexcept {
91   return compare_three_way()(__x.address(), __y.address());
92 }
93 
94 template <class _Promise>
95 struct _LIBCPP_TEMPLATE_VIS coroutine_handle {
96 public:
97   // [coroutine.handle.con], construct/reset
98   constexpr coroutine_handle() noexcept = default;
99 
100   _LIBCPP_HIDE_FROM_ABI constexpr coroutine_handle(nullptr_t) noexcept {}
101 
102   _LIBCPP_HIDE_FROM_ABI static coroutine_handle from_promise(_Promise& __promise) {
103     using _RawPromise = __remove_cv_t<_Promise>;
104     coroutine_handle __tmp;
105     __tmp.__handle_ =
106         __builtin_coro_promise(std::addressof(const_cast<_RawPromise&>(__promise)), alignof(_Promise), true);
107     return __tmp;
108   }
109 
110   _LIBCPP_HIDE_FROM_ABI coroutine_handle& operator=(nullptr_t) noexcept {
111     __handle_ = nullptr;
112     return *this;
113   }
114 
115   // [coroutine.handle.export.import], export/import
116   _LIBCPP_HIDE_FROM_ABI constexpr void* address() const noexcept { return __handle_; }
117 
118   _LIBCPP_HIDE_FROM_ABI static constexpr coroutine_handle from_address(void* __addr) noexcept {
119     coroutine_handle __tmp;
120     __tmp.__handle_ = __addr;
121     return __tmp;
122   }
123 
124   // [coroutine.handle.conv], conversion
125   _LIBCPP_HIDE_FROM_ABI constexpr operator coroutine_handle<>() const noexcept {
126     return coroutine_handle<>::from_address(address());
127   }
128 
129   // [coroutine.handle.observers], observers
130   _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __handle_ != nullptr; }
131 
132   _LIBCPP_HIDE_FROM_ABI bool done() const {
133     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "done() can be called only on suspended coroutines");
134     return __builtin_coro_done(__handle_);
135   }
136 
137   // [coroutine.handle.resumption], resumption
138   _LIBCPP_HIDE_FROM_ABI void operator()() const { resume(); }
139 
140   _LIBCPP_HIDE_FROM_ABI void resume() const {
141     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "resume() can be called only on suspended coroutines");
142     _LIBCPP_ASSERT_UNCATEGORIZED(!done(), "resume() has undefined behavior when the coroutine is done");
143     __builtin_coro_resume(__handle_);
144   }
145 
146   _LIBCPP_HIDE_FROM_ABI void destroy() const {
147     _LIBCPP_ASSERT_UNCATEGORIZED(__is_suspended(), "destroy() can be called only on suspended coroutines");
148     __builtin_coro_destroy(__handle_);
149   }
150 
151   // [coroutine.handle.promise], promise access
152   _LIBCPP_HIDE_FROM_ABI _Promise& promise() const {
153     return *static_cast<_Promise*>(__builtin_coro_promise(this->__handle_, alignof(_Promise), false));
154   }
155 
156 private:
157   _LIBCPP_HIDE_FROM_ABI bool __is_suspended() const {
158     // FIXME actually implement a check for if the coro is suspended.
159     return __handle_ != nullptr;
160   }
161   void* __handle_ = nullptr;
162 };
163 
164 // [coroutine.handle.hash]
165 template <class _Tp>
166 struct hash<coroutine_handle<_Tp>> {
167   _LIBCPP_HIDE_FROM_ABI size_t operator()(const coroutine_handle<_Tp>& __v) const noexcept {
168     return hash<void*>()(__v.address());
169   }
170 };
171 
172 _LIBCPP_END_NAMESPACE_STD
173 
174 #endif // __LIBCPP_STD_VER >= 20
175 
176 #endif // _LIBCPP___COROUTINE_COROUTINE_HANDLE_H
177