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