// -*- C++ -*- //===----------------------------- coroutine -----------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef _LIBCPP_EXPERIMENTAL_COROUTINE #define _LIBCPP_EXPERIMENTAL_COROUTINE /** experimental/coroutine synopsis // C++next namespace std { namespace experimental { inline namespace coroutines_v1 { // 18.11.1 coroutine traits template class coroutine_traits; // 18.11.2 coroutine handle template class coroutine_handle; // 18.11.2.7 comparison operators: bool operator==(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; bool operator!=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; bool operator<(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; bool operator<=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; bool operator>=(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; bool operator>(coroutine_handle<> x, coroutine_handle<> y) _NOEXCEPT; // 18.11.3 trivial awaitables struct suspend_never; struct suspend_always; // 18.11.2.8 hash support: template struct hash; template struct hash>; } // namespace coroutines_v1 } // namespace experimental } // namespace std */ #include #include #include #include #include // for hash #include #include #include <__debug> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) #pragma GCC system_header #endif #ifdef _LIBCPP_HAS_NO_COROUTINES # if defined(_LIBCPP_WARNING) _LIBCPP_WARNING(" cannot be used with this compiler") # else # warning cannot be used with this compiler # endif #endif #ifndef _LIBCPP_HAS_NO_COROUTINES _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_COROUTINES template struct __coroutine_traits_sfinae {}; template struct __coroutine_traits_sfinae< _Tp, typename __void_t::type> { using promise_type = typename _Tp::promise_type; }; template struct _LIBCPP_TEMPLATE_VIS coroutine_traits : public __coroutine_traits_sfinae<_Ret> { }; template class _LIBCPP_TEMPLATE_VIS coroutine_handle; template <> class _LIBCPP_TEMPLATE_VIS coroutine_handle { public: _LIBCPP_ALWAYS_INLINE _LIBCPP_CONSTEXPR coroutine_handle() _NOEXCEPT : __handle_(nullptr) {} _LIBCPP_ALWAYS_INLINE _LIBCPP_CONSTEXPR coroutine_handle(nullptr_t) _NOEXCEPT : __handle_(nullptr) {} _LIBCPP_ALWAYS_INLINE coroutine_handle& operator=(nullptr_t) _NOEXCEPT { __handle_ = nullptr; return *this; } _LIBCPP_ALWAYS_INLINE _LIBCPP_CONSTEXPR void* address() const _NOEXCEPT { return __handle_; } _LIBCPP_ALWAYS_INLINE _LIBCPP_CONSTEXPR explicit operator bool() const _NOEXCEPT { return __handle_; } _LIBCPP_ALWAYS_INLINE void operator()() { resume(); } _LIBCPP_ALWAYS_INLINE void resume() { _LIBCPP_ASSERT(__is_suspended(), "resume() can only be called on suspended coroutines"); _LIBCPP_ASSERT(!done(), "resume() has undefined behavior when the coroutine is done"); __builtin_coro_resume(__handle_); } _LIBCPP_ALWAYS_INLINE void destroy() { _LIBCPP_ASSERT(__is_suspended(), "destroy() can only be called on suspended coroutines"); __builtin_coro_destroy(__handle_); } _LIBCPP_ALWAYS_INLINE bool done() const { _LIBCPP_ASSERT(__is_suspended(), "done() can only be called on suspended coroutines"); return __builtin_coro_done(__handle_); } public: _LIBCPP_ALWAYS_INLINE static coroutine_handle from_address(void* __addr) _NOEXCEPT { coroutine_handle __tmp; __tmp.__handle_ = __addr; return __tmp; } // FIXME: Should from_address(nullptr) be allowed? _LIBCPP_ALWAYS_INLINE static coroutine_handle from_address(nullptr_t) _NOEXCEPT { return coroutine_handle(nullptr); } template static coroutine_handle from_address(_Tp*) { static_assert(_CallIsValid, "coroutine_handle::from_address cannot be called with " "non-void pointers"); } private: bool __is_suspended() const _NOEXCEPT { // FIXME actually implement a check for if the coro is suspended. return __handle_; } template friend class coroutine_handle; void* __handle_; }; // 18.11.2.7 comparison operators: inline _LIBCPP_ALWAYS_INLINE bool operator==(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return __x.address() == __y.address(); } inline _LIBCPP_ALWAYS_INLINE bool operator!=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return !(__x == __y); } inline _LIBCPP_ALWAYS_INLINE bool operator<(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return less()(__x.address(), __y.address()); } inline _LIBCPP_ALWAYS_INLINE bool operator>(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return __y < __x; } inline _LIBCPP_ALWAYS_INLINE bool operator<=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return !(__x > __y); } inline _LIBCPP_ALWAYS_INLINE bool operator>=(coroutine_handle<> __x, coroutine_handle<> __y) _NOEXCEPT { return !(__x < __y); } template class _LIBCPP_TEMPLATE_VIS coroutine_handle : public coroutine_handle<> { using _Base = coroutine_handle<>; public: #ifndef _LIBCPP_CXX03_LANG // 18.11.2.1 construct/reset using coroutine_handle<>::coroutine_handle; #else _LIBCPP_ALWAYS_INLINE coroutine_handle() _NOEXCEPT : _Base() {} _LIBCPP_ALWAYS_INLINE coroutine_handle(nullptr_t) _NOEXCEPT : _Base(nullptr) {} #endif _LIBCPP_INLINE_VISIBILITY coroutine_handle& operator=(nullptr_t) _NOEXCEPT { _Base::operator=(nullptr); return *this; } _LIBCPP_INLINE_VISIBILITY _Promise& promise() const { return *reinterpret_cast<_Promise*>( __builtin_coro_promise(this->__handle_, __alignof(_Promise), false)); } public: _LIBCPP_ALWAYS_INLINE static coroutine_handle from_address(void* __addr) _NOEXCEPT { coroutine_handle __tmp; __tmp.__handle_ = __addr; return __tmp; } // NOTE: this overload isn't required by the standard but is needed so // the deleted _Promise* overload doesn't make from_address(nullptr) // ambiguous. // FIXME: should from_address work with nullptr? _LIBCPP_ALWAYS_INLINE static coroutine_handle from_address(nullptr_t) _NOEXCEPT { return coroutine_handle(nullptr); } template static coroutine_handle from_address(_Tp*) { static_assert(_CallIsValid, "coroutine_handle::from_address cannot be called with " "non-void pointers"); } template static coroutine_handle from_address(_Promise*) { static_assert(_CallIsValid, "coroutine_handle::from_address cannot be used with " "pointers to the coroutine's promise type; use 'from_promise' instead"); } _LIBCPP_ALWAYS_INLINE static coroutine_handle from_promise(_Promise& __promise) _NOEXCEPT { typedef typename remove_cv<_Promise>::type _RawPromise; coroutine_handle __tmp; __tmp.__handle_ = __builtin_coro_promise( _VSTD::addressof(const_cast<_RawPromise&>(__promise)), __alignof(_Promise), true); return __tmp; } }; struct _LIBCPP_TYPE_VIS suspend_never { _LIBCPP_ALWAYS_INLINE bool await_ready() const _NOEXCEPT { return true; } _LIBCPP_ALWAYS_INLINE void await_suspend(coroutine_handle<>) const _NOEXCEPT {} _LIBCPP_ALWAYS_INLINE void await_resume() const _NOEXCEPT {} }; struct _LIBCPP_TYPE_VIS suspend_always { _LIBCPP_ALWAYS_INLINE bool await_ready() const _NOEXCEPT { return false; } _LIBCPP_ALWAYS_INLINE void await_suspend(coroutine_handle<>) const _NOEXCEPT {} _LIBCPP_ALWAYS_INLINE void await_resume() const _NOEXCEPT {} }; _LIBCPP_END_NAMESPACE_EXPERIMENTAL_COROUTINES _LIBCPP_BEGIN_NAMESPACE_STD template struct hash<_VSTD_CORO::coroutine_handle<_Tp> > { using __arg_type = _VSTD_CORO::coroutine_handle<_Tp>; _LIBCPP_INLINE_VISIBILITY size_t operator()(__arg_type const& __v) const _NOEXCEPT {return hash()(__v.address());} }; _LIBCPP_END_NAMESPACE_STD #endif // !defined(_LIBCPP_HAS_NO_COROUTINES) #endif /* _LIBCPP_EXPERIMENTAL_COROUTINE */