// coroutine experimental header // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // Library support of coroutines TS, https://wg21.link/p0057 #pragma once #ifndef _EXPERIMENTAL_COROUTINE_ #define _EXPERIMENTAL_COROUTINE_ #include #if _STL_COMPILER_PREPROCESSOR #include #include #if _HAS_EXCEPTIONS #include #endif #include #include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new #if defined(__clang__) && !defined(_SILENCE_CLANG_COROUTINE_MESSAGE) #error The , , and headers currently do not \ support Clang. You can define _SILENCE_CLANG_COROUTINE_MESSAGE to silence this message and acknowledge that this is \ unsupported. #endif // defined(__clang__) && !defined(_SILENCE_CLANG_COROUTINE_MESSAGE) #ifdef __cpp_impl_coroutine #error The and headers are only supported \ with /await and implement pre-C++20 coroutine support. Use for standard C++20 coroutines. #endif // __cpp_impl_coroutine // intrinsics used in implementation of coroutine_handle extern "C" size_t _coro_resume(void*); extern "C" void _coro_destroy(void*); extern "C" size_t _coro_done(void*); #pragma intrinsic(_coro_resume) #pragma intrinsic(_coro_destroy) #pragma intrinsic(_coro_done) #ifndef _ALLOW_COROUTINE_ABI_MISMATCH #pragma detect_mismatch("_COROUTINE_ABI", "1") #endif // _ALLOW_COROUTINE_ABI_MISMATCH _STD_BEGIN namespace experimental { template struct _Coroutine_traits_sfinae {}; template struct _Coroutine_traits_sfinae<_Ret, void_t> { using promise_type = typename _Ret::promise_type; }; template struct coroutine_traits : _Coroutine_traits_sfinae<_Ret> {}; template struct coroutine_handle; template <> struct coroutine_handle { // no promise access coroutine_handle() noexcept {} coroutine_handle(nullptr_t) noexcept {} coroutine_handle& operator=(nullptr_t) noexcept { _Ptr = nullptr; return *this; } static coroutine_handle from_address(void* _Addr) noexcept { coroutine_handle _Result; _Result._Ptr = static_cast<_Resumable_frame_prefix*>(_Addr); return _Result; } void* address() const noexcept { return _Ptr; } [[deprecated("coroutine_handle::to_address() is deprecated. " "Use coroutine_handle::address() instead.")]] void* to_address() const noexcept { return _Ptr; } void operator()() const { resume(); } explicit operator bool() const noexcept { return _Ptr != nullptr; } void resume() const { _coro_resume(_Ptr); } void destroy() { _coro_destroy(_Ptr); } bool done() const { // REVISIT: should return _coro_done() == 0; when intrinsic is // hooked up return _Ptr->_Index == 0; } struct _Resumable_frame_prefix { using _Resume_fn = void(__cdecl*)(void*); _Resume_fn _Fn; uint16_t _Index; uint16_t _Flags; }; protected: _Resumable_frame_prefix* _Ptr = nullptr; }; template struct coroutine_handle : coroutine_handle<> { // general form using coroutine_handle<>::coroutine_handle; [[deprecated("coroutine_handle::from_promise(T*) with pointer parameter is deprecated. " "Use coroutine_handle::from_promise(T&) instead.")]] static coroutine_handle from_promise(_PromiseT* _Prom) noexcept { return from_promise(*_Prom); } static coroutine_handle from_promise(_PromiseT& _Prom) noexcept { auto _FramePtr = reinterpret_cast(_STD addressof(_Prom)) + _ALIGNED_SIZE; coroutine_handle<_PromiseT> _Result; _Result._Ptr = reinterpret_cast<_Resumable_frame_prefix*>(_FramePtr); return _Result; } static coroutine_handle from_address(void* _Addr) noexcept { coroutine_handle _Result; _Result._Ptr = static_cast<_Resumable_frame_prefix*>(_Addr); return _Result; } coroutine_handle& operator=(nullptr_t) noexcept { _Ptr = nullptr; return *this; } static const size_t _ALIGN_REQ = sizeof(void*) * 2; static const size_t _ALIGNED_SIZE = is_empty_v<_PromiseT> ? 0 : ((sizeof(_PromiseT) + _ALIGN_REQ - 1) & ~(_ALIGN_REQ - 1)); _PromiseT& promise() const noexcept { return *const_cast<_PromiseT*>( reinterpret_cast(reinterpret_cast(_Ptr) - _ALIGNED_SIZE)); } }; _NODISCARD inline bool operator==(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return _Left.address() == _Right.address(); } _NODISCARD inline bool operator<(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return less()(_Left.address(), _Right.address()); } _NODISCARD inline bool operator!=(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return !(_Left == _Right); } _NODISCARD inline bool operator>(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return _Right < _Left; } _NODISCARD inline bool operator<=(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return !(_Left > _Right); } _NODISCARD inline bool operator>=(coroutine_handle<> _Left, coroutine_handle<> _Right) noexcept { return !(_Left < _Right); } struct suspend_if { bool _Ready; explicit suspend_if(bool _Condition) noexcept : _Ready(!_Condition) {} bool await_ready() noexcept { return _Ready; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; struct suspend_always { bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; struct suspend_never { bool await_ready() noexcept { return true; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; // _Resumable_helper_traits class isolates front-end from public surface // naming changes template struct _Resumable_helper_traits { using _Traits = coroutine_traits<_Ret, _Ts...>; using _PromiseT = typename _Traits::promise_type; using _Handle_type = coroutine_handle<_PromiseT>; static _PromiseT* _Promise_from_frame(void* _Addr) noexcept { return reinterpret_cast<_PromiseT*>(reinterpret_cast(_Addr) - _Handle_type::_ALIGNED_SIZE); } static _Handle_type _Handle_from_frame(void* _Addr) noexcept { return _Handle_type::from_promise(*_Promise_from_frame(_Addr)); } static void _Set_exception(void* _Addr) { _Promise_from_frame(_Addr)->set_exception(_STD current_exception()); } static void _ConstructPromise(void* _Addr, void* _Resume_addr, int _HeapElision) { *reinterpret_cast(_Addr) = _Resume_addr; *reinterpret_cast(reinterpret_cast(_Addr) + sizeof(void*)) = 2u + (_HeapElision ? 0u : 0x10000u); auto _Prom = _Promise_from_frame(_Addr); ::new (static_cast(_Prom)) _PromiseT(); } static void _DestructPromise(void* _Addr) { _Promise_from_frame(_Addr)->~_PromiseT(); } }; } // namespace experimental _STD_END // resumable functions support intrinsics extern "C" size_t _coro_frame_size(); extern "C" void* _coro_frame_ptr(); extern "C" void _coro_init_block(); extern "C" void* _coro_resume_addr(); extern "C" void _coro_init_frame(void*); extern "C" void _coro_save(size_t); extern "C" void _coro_suspend(size_t); extern "C" void _coro_cancel(); extern "C" void _coro_resume_block(); #pragma intrinsic(_coro_frame_size) #pragma intrinsic(_coro_frame_ptr) #pragma intrinsic(_coro_init_block) #pragma intrinsic(_coro_resume_addr) #pragma intrinsic(_coro_init_frame) #pragma intrinsic(_coro_save) #pragma intrinsic(_coro_suspend) #pragma intrinsic(_coro_cancel) #pragma intrinsic(_coro_resume_block) #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) #endif // _STL_COMPILER_PREPROCESSOR #endif // _EXPERIMENTAL_COROUTINE_