1 // -*- C++ -*- 2 //===----------------------------- coroutine -----------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef _LIBCPP_EXPERIMENTAL_COROUTINE 11 #define _LIBCPP_EXPERIMENTAL_COROUTINE 12 13 /** 14 experimental/coroutine synopsis 15 16 // C++next 17 18 namespace std { 19 namespace experimental { 20 inline namespace coroutines_v1 { 21 22 // 18.11.1 coroutine traits 23 template <typename R, typename... ArgTypes> 24 class coroutine_traits; 25 // 18.11.2 coroutine handle 26 template <typename Promise = void> 27 class coroutine_handle; 28 // 18.11.2.7 comparison operators: 29 bool operator==(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 30 bool operator!=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 31 bool operator<(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 32 bool operator<=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 33 bool operator>=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 34 bool operator>(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; 35 // 18.11.3 trivial awaitables 36 struct suspend_never; 37 struct suspend_always; 38 // 18.11.2.8 hash support: 39 template <class T> struct hash; 40 template <class P> struct hash<coroutine_handle<P>>; 41 42 } // namespace coroutines_v1 43 } // namespace experimental 44 } // namespace std 45 46 */ 47 48 #include <experimental/__config> 49 #include <new> 50 #include <type_traits> 51 #include <functional> 52 #include <memory> // for hash<T*> 53 #include <cstddef> 54 #include <__debug> 55 56 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 57 #pragma GCC system_header 58 #endif 59 60 #ifdef _LIBCPP_HAS_NO_COROUTINES 61 # if defined(_LIBCPP_WARNING) 62 _LIBCPP_WARNING("<experimental/coroutine> cannot be used with this compiler") 63 # else 64 # warning <experimental/coroutine> cannot be used with this compiler 65 # endif 66 #endif 67 68 #ifndef _LIBCPP_HAS_NO_COROUTINES 69 70 _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES 71 72 template <class _Tp, class = void> 73 struct __coroutine_traits_sfinae {}; 74 75 template <class _Tp> 76 struct __coroutine_traits_sfinae< 77 _Tp, typename __void_t<typename _Tp::promise_type>::type> 78 { 79 using promise_type = typename _Tp::promise_type; 80 }; 81 82 template <typename _Ret, typename... _Args> 83 struct coroutine_traits 84 : public __coroutine_traits_sfinae<_Ret> 85 { 86 }; 87 88 template <typename _Promise = void> 89 class _LIBCPP_TEMPLATE_VIS coroutine_handle; 90 91 template <> 92 class _LIBCPP_TEMPLATE_VIS coroutine_handle<void> { 93 public: 94 _LIBCPP_INLINE_VISIBILITY 95 _LIBCPP_CONSTEXPR coroutine_handle() _NOEXCEPT : __handle_(nullptr) {} 96 97 _LIBCPP_INLINE_VISIBILITY 98 _LIBCPP_CONSTEXPR coroutine_handle(nullptr_t) _NOEXCEPT : __handle_(nullptr) {} 99 100 _LIBCPP_INLINE_VISIBILITY 101 coroutine_handle& operator=(nullptr_t) _NOEXCEPT { 102 __handle_ = nullptr; 103 return *this; 104 } 105 106 _LIBCPP_INLINE_VISIBILITY 107 _LIBCPP_CONSTEXPR void* address() const _NOEXCEPT { return __handle_; } 108 109 _LIBCPP_INLINE_VISIBILITY 110 _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return __handle_; } 111 112 _LIBCPP_INLINE_VISIBILITY 113 void operator()() { resume(); } 114 115 _LIBCPP_INLINE_VISIBILITY 116 void resume() { 117 _LIBCPP_ASSERT(__is_suspended(), 118 "resume() can only be called on suspended coroutines"); 119 _LIBCPP_ASSERT(!done(), 120 "resume() has undefined behavior when the coroutine is done"); 121 __builtin_coro_resume(__handle_); 122 } 123 124 _LIBCPP_INLINE_VISIBILITY 125 void destroy() { 126 _LIBCPP_ASSERT(__is_suspended(), 127 "destroy() can only be called on suspended coroutines"); 128 __builtin_coro_destroy(__handle_); 129 } 130 131 _LIBCPP_INLINE_VISIBILITY 132 bool done() const { 133 _LIBCPP_ASSERT(__is_suspended(), 134 "done() can only be called on suspended coroutines"); 135 return __builtin_coro_done(__handle_); 136 } 137 138 public: 139 _LIBCPP_INLINE_VISIBILITY 140 static coroutine_handle from_address(void* __addr) _NOEXCEPT { 141 coroutine_handle __tmp; 142 __tmp.__handle_ = __addr; 143 return __tmp; 144 } 145 146 // FIXME: Should from_address(nullptr) be allowed? 147 _LIBCPP_INLINE_VISIBILITY 148 static coroutine_handle from_address(nullptr_t) _NOEXCEPT { 149 return coroutine_handle(nullptr); 150 } 151 152 template <class _Tp, bool _CallIsValid = false> 153 static coroutine_handle from_address(_Tp*) { 154 static_assert(_CallIsValid, 155 "coroutine_handle<void>::from_address cannot be called with " 156 "non-void pointers"); 157 } 158 159 private: 160 bool __is_suspended() const _NOEXCEPT { 161 // FIXME actually implement a check for if the coro is suspended. 162 return __handle_; 163 } 164 165 template <class _PromiseT> friend class coroutine_handle; 166 void* __handle_; 167 }; 168 169 // 18.11.2.7 comparison operators: 170 inline _LIBCPP_INLINE_VISIBILITY 171 bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 172 return __x.address() == __y.address(); 173 } 174 inline _LIBCPP_INLINE_VISIBILITY 175 bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 176 return !(__x == __y); 177 } 178 inline _LIBCPP_INLINE_VISIBILITY 179 bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 180 return less<void*>()(__x.address(), __y.address()); 181 } 182 inline _LIBCPP_INLINE_VISIBILITY 183 bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 184 return __y < __x; 185 } 186 inline _LIBCPP_INLINE_VISIBILITY 187 bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 188 return !(__x > __y); 189 } 190 inline _LIBCPP_INLINE_VISIBILITY 191 bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { 192 return !(__x < __y); 193 } 194 195 template <typename _Promise> 196 class _LIBCPP_TEMPLATE_VIS coroutine_handle : public coroutine_handle<> { 197 using _Base = coroutine_handle<>; 198 public: 199 #ifndef _LIBCPP_CXX03_LANG 200 // 18.11.2.1 construct/reset 201 using coroutine_handle<>::coroutine_handle; 202 #else 203 _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT : _Base() {} 204 _LIBCPP_INLINE_VISIBILITY coroutine_handle(nullptr_t) _NOEXCEPT : _Base(nullptr) {} 205 #endif 206 _LIBCPP_INLINE_VISIBILITY 207 coroutine_handle& operator=(nullptr_t) _NOEXCEPT { 208 _Base::operator=(nullptr); 209 return *this; 210 } 211 212 _LIBCPP_INLINE_VISIBILITY 213 _Promise& promise() const { 214 return *static_cast<_Promise*>( 215 __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false)); 216 } 217 218 public: 219 _LIBCPP_INLINE_VISIBILITY 220 static coroutine_handle from_address(void* __addr) _NOEXCEPT { 221 coroutine_handle __tmp; 222 __tmp.__handle_ = __addr; 223 return __tmp; 224 } 225 226 // NOTE: this overload isn't required by the standard but is needed so 227 // the deleted _Promise* overload doesn't make from_address(nullptr) 228 // ambiguous. 229 // FIXME: should from_address work with nullptr? 230 _LIBCPP_INLINE_VISIBILITY 231 static coroutine_handle from_address(nullptr_t) _NOEXCEPT { 232 return coroutine_handle(nullptr); 233 } 234 235 template <class _Tp, bool _CallIsValid = false> 236 static coroutine_handle from_address(_Tp*) { 237 static_assert(_CallIsValid, 238 "coroutine_handle<promise_type>::from_address cannot be called with " 239 "non-void pointers"); 240 } 241 242 template <bool _CallIsValid = false> 243 static coroutine_handle from_address(_Promise*) { 244 static_assert(_CallIsValid, 245 "coroutine_handle<promise_type>::from_address cannot be used with " 246 "pointers to the coroutine's promise type; use 'from_promise' instead"); 247 } 248 249 _LIBCPP_INLINE_VISIBILITY 250 static coroutine_handle from_promise(_Promise& __promise) _NOEXCEPT { 251 typedef typename remove_cv<_Promise>::type _RawPromise; 252 coroutine_handle __tmp; 253 __tmp.__handle_ = __builtin_coro_promise( 254 _VSTD::addressof(const_cast<_RawPromise&>(__promise)), 255 _LIBCPP_ALIGNOF(_Promise), true); 256 return __tmp; 257 } 258 }; 259 260 #if __has_builtin(__builtin_coro_noop) 261 struct noop_coroutine_promise {}; 262 263 template <> 264 class _LIBCPP_TEMPLATE_VIS coroutine_handle<noop_coroutine_promise> 265 : public coroutine_handle<> { 266 using _Base = coroutine_handle<>; 267 using _Promise = noop_coroutine_promise; 268 public: 269 270 _LIBCPP_INLINE_VISIBILITY 271 _Promise& promise() const { 272 return *static_cast<_Promise*>( 273 __builtin_coro_promise(this->__handle_, _LIBCPP_ALIGNOF(_Promise), false)); 274 } 275 276 _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return true; } 277 _LIBCPP_CONSTEXPR bool done() const _NOEXCEPT { return false; } 278 279 _LIBCPP_CONSTEXPR_AFTER_CXX17 void operator()() const _NOEXCEPT {} 280 _LIBCPP_CONSTEXPR_AFTER_CXX17 void resume() const _NOEXCEPT {} 281 _LIBCPP_CONSTEXPR_AFTER_CXX17 void destroy() const _NOEXCEPT {} 282 283 private: 284 _LIBCPP_INLINE_VISIBILITY 285 friend coroutine_handle<noop_coroutine_promise> noop_coroutine() _NOEXCEPT; 286 287 _LIBCPP_INLINE_VISIBILITY coroutine_handle() _NOEXCEPT { 288 this->__handle_ = __builtin_coro_noop(); 289 } 290 }; 291 292 using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>; 293 294 inline _LIBCPP_INLINE_VISIBILITY 295 noop_coroutine_handle noop_coroutine() _NOEXCEPT { 296 return noop_coroutine_handle(); 297 } 298 #endif // __has_builtin(__builtin_coro_noop) 299 300 struct suspend_never { 301 _LIBCPP_INLINE_VISIBILITY 302 bool await_ready() const _NOEXCEPT { return true; } 303 _LIBCPP_INLINE_VISIBILITY 304 void await_suspend(coroutine_handle<>) const _NOEXCEPT {} 305 _LIBCPP_INLINE_VISIBILITY 306 void await_resume() const _NOEXCEPT {} 307 }; 308 309 struct suspend_always { 310 _LIBCPP_INLINE_VISIBILITY 311 bool await_ready() const _NOEXCEPT { return false; } 312 _LIBCPP_INLINE_VISIBILITY 313 void await_suspend(coroutine_handle<>) const _NOEXCEPT {} 314 _LIBCPP_INLINE_VISIBILITY 315 void await_resume() const _NOEXCEPT {} 316 }; 317 318 _LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES 319 320 _LIBCPP_BEGIN_NAMESPACE_STD 321 322 template <class _Tp> 323 struct hash<_VSTD_CORO::coroutine_handle<_Tp> > { 324 using __arg_type = _VSTD_CORO::coroutine_handle<_Tp>; 325 _LIBCPP_INLINE_VISIBILITY 326 size_t operator()(__arg_type const& __v) const _NOEXCEPT 327 {return hash<void*>()(__v.address());} 328 }; 329 330 _LIBCPP_END_NAMESPACE_STD 331 332 #endif // !defined(_LIBCPP_HAS_NO_COROUTINES) 333 334 #endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */ 335