/*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * pplawait.h * * Await Compiler Support for Parallel Patterns Library - PPL Tasks * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #ifndef _PPLAWAIT_H #define _PPLAWAIT_H #ifdef __cpp_impl_coroutine #include #else // ^^^ C++20 coroutines / TS coroutines vvv #include #endif // ^^^ TS coroutines ^^^ #include #include #include #include #include #define __resumable _STD_BEGIN #ifndef __cpp_impl_coroutine namespace experimental { #endif // __cpp_impl_coroutine template struct coroutine_traits<::concurrency::task<_Ty>, _Whatever...> { struct promise_type { auto get_return_object() const { return ::concurrency::create_task(_M_tce); } suspend_never initial_suspend() const noexcept { return {}; } suspend_never final_suspend() const noexcept { return {}; } void return_value(const _Ty& _Val) { _M_tce.set(_Val); } void unhandled_exception() { _M_tce.set_exception(_STD current_exception()); } private: ::concurrency::task_completion_event<_Ty> _M_tce; }; }; template struct coroutine_traits<::concurrency::task, _Whatever...> { struct promise_type { auto get_return_object() const { return ::concurrency::create_task(_M_tce); } suspend_never initial_suspend() const noexcept { return {}; } suspend_never final_suspend() const noexcept { return {}; } void return_void() { _M_tce.set(); } void unhandled_exception() { _M_tce.set_exception(_STD current_exception()); } private: ::concurrency::task_completion_event _M_tce; }; }; #ifndef __cpp_impl_coroutine } // namespace experimental #endif // __cpp_impl_coroutine _STD_END #ifdef __cplusplus_winrt namespace Windows { namespace Foundation { // When GetResults is called for an operation in the Canceled state, an E_ILLEGAL_METHOD_CALL exception will be // thrown. We preempt that by throwing an OperationCanceled exception, which better meets caller expectations. inline void _VerifyStateForResultsCall(Windows::Foundation::AsyncStatus _Status) { if (_Status == AsyncStatus::Canceled) { throw ::Platform::Exception::CreateException(E_ABORT); } } } // namespace Foundation } // namespace Windows #endif // __cplusplus_winrt #ifndef _RESUMABLE_ENABLE_LEGACY_AWAIT_ADAPTERS namespace Concurrency { #if defined(__cpp_impl_coroutine) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED) template struct _Ppltask_awaiter { task<_Ty>& _Task; bool await_ready() const { return _Task.is_done(); } void await_suspend(_STD #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED experimental:: #endif coroutine_handle<> _ResumeCb) { _Task.then( [_ResumeCb](const task<_Ty>&) { _ResumeCb(); }, task_continuation_context::get_current_winrt_context()); } decltype(auto) await_resume() { return _Task.get(); } }; template auto operator co_await(task<_Ty>&& _Task) { return _Ppltask_awaiter<_Ty>{_Task}; } template auto operator co_await(task<_Ty>& _Task) { return _Ppltask_awaiter<_Ty>{_Task}; } #endif // defined(__cpp_impl_coroutine) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED) struct [[deprecated("warning PPL4001: " "The await_resume_context class is deprecated and will be removed in a future release. " "You can define _RESUMABLE_ENABLE_LEGACY_AWAIT_ADAPTERS and add /await to the command line " "to acknowledge that you have received this warning and get access to " "await_resume_context.")]] await_resume_context{}; } // namespace Concurrency // The Windows-internal CXAwait.h provides alternative bindings for IAsyncXXX and coroutines. These defines ensure the // headers are included in the correct order, cxawait.h should be first. #if defined(__cplusplus_winrt) && (defined(__cpp_impl_coroutine) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)) \ && !defined(_COAWAIT_FROM_CXAWAIT) #pragma detect_mismatch("_COAWAIT_FROM_CXAWAIT", "INCLUDE CXAWAIT FIRST") namespace Windows { namespace Foundation { template struct _IAsync_awaiter { _TaskTy ^ _Task; bool await_ready() const { return _Task->Status >= AsyncStatus::Completed; } void await_suspend(_STD #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED experimental:: #endif coroutine_handle<> _ResumeCb) { _Task->Completed = ref new _HandlerTy( [_ResumeCb](_TaskTy ^, AsyncStatus) { _ResumeCb(); }, ::Platform::CallbackContext::Same); } auto await_resume() { _VerifyStateForResultsCall(_Task->Status); return _Task->GetResults(); } }; inline auto operator co_await(IAsyncAction ^ _Expr) { return _IAsync_awaiter{_Expr}; } template auto operator co_await(IAsyncActionWithProgress<_Pr> ^ _Expr) { return _IAsync_awaiter, AsyncActionWithProgressCompletedHandler<_Pr>>{_Expr}; } template auto operator co_await(IAsyncOperation<_Ty> ^ _Expr) { return _IAsync_awaiter, AsyncOperationCompletedHandler<_Ty>>{_Expr}; } template auto operator co_await(IAsyncOperationWithProgress<_Ty, _Pr> ^ _Expr) { return _IAsync_awaiter, AsyncOperationWithProgressCompletedHandler<_Ty, _Pr>>{_Expr}; } } // namespace Foundation } // namespace Windows #endif // defined(__cplusplus_winrt) && (defined(__cpp_impl_coroutine) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)) #else // _RESUMABLE_ENABLE_LEGACY_AWAIT_ADAPTERS _STD_BEGIN namespace experimental { namespace details { namespace awaitabletraitsimpl { template inline bool _TryAwaitReady(_Ty& _Obj, decltype(_STD declval<_Ty>().await_ready(), 0), int) { return _Obj.await_ready(); } template inline bool _TryAwaitReady(_Ty& _Obj, decltype(await_ready(_STD declval<_Ty>()), 0), ...) { return await_ready(_Obj); } template inline void _TryAwaitSuspend( _Ty& _Obj, _Handle _Hnd, decltype(_STD declval<_Ty>().await_suspend(_STD declval<_Handle>()), 0), int) { _Obj.await_suspend(_Hnd); } template inline void _TryAwaitSuspend(_Ty& _Obj, _Handle _Hnd, decltype(await_suspend(_STD declval<_Ty>(), _STD declval<_Handle>()), 0), ...) { await_suspend(_Obj, _Hnd); } template inline auto _TryAwaitResume(_Ty& _Obj, decltype(_STD declval<_Ty>().await_resume(), 0), int) { return _Obj.await_resume(); } template inline auto _TryAwaitResume(_Ty& _Obj, decltype(await_resume(_STD declval<_Ty>()), 0), ...) { return await_resume(_Obj); } template inline true_type _TryAwaitable(_Ty, decltype(_TryAwaitReady(_STD declval>(), 0, 0), _TryAwaitSuspend(_STD declval>(), _STD #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED experimental:: #endif coroutine_handle<>(), 0, 0), _TryAwaitResume(_STD declval>(), 0, 0), 0)); inline false_type _TryAwaitable(...); } // namespace awaitabletraitsimpl } // namespace details // Traits for // 1. detecting whether a type satisfies awaitable contract requirement // 2. invoking its awaitable contract methods template struct awaitable_traits : decltype(details::awaitabletraitsimpl::_TryAwaitable(_STD declval<_Ty>(), 0)) { static bool invoke_await_ready(_Ty& _Obj) { return details::awaitabletraitsimpl::_TryAwaitReady(_Obj, 0, 0); } template static void invoke_await_suspend(_Ty& _Obj, _Handle _Hnd) { details::awaitabletraitsimpl::_TryAwaitSuspend(_Obj, _Hnd, 0, 0); } static auto invoke_await_resume(_Ty& _Obj) { return details::awaitabletraitsimpl::_TryAwaitResume(_Obj, 0, 0); } }; template using is_awaitable = awaitable_traits<_Ty>; template using is_awaitable_t = typename awaitable_traits<_Ty>::type; } // namespace experimental _STD_END namespace Concurrency { // PPLTask awaitable extension template bool await_ready(const task<_Ty>& _Task) { return _Task.is_done(); } template void await_suspend(task<_Ty>& _Task, _Handle _ResumeCb) { _Task.then( [_ResumeCb](const task<_Ty>&) { _ResumeCb(); }, task_continuation_context::get_current_winrt_context()); } template auto await_resume(const task<_Ty>& _Task) { return _Task.get(); } class await_resume_context { class ThreadpoolContext { details::_Threadpool_chore _M_chore; static void __cdecl _ChoreCallback(void* _TpTask) { auto _Context = static_cast(_TpTask); _Context->_M_func(); } _STD function _M_func; public: template void _CallInContext(_Handle _Hnd) { _M_func = _Hnd; _M_chore._M_callback = &ThreadpoolContext::_ChoreCallback; _M_chore._M_data = this; details::_Schedule_chore(&_M_chore); } ThreadpoolContext() { _M_chore._M_work = nullptr; } ~ThreadpoolContext() { // Releasing chore multiple times is fine details::_Release_chore(&_M_chore); } }; details::_ContextCallback _M_context; ThreadpoolContext _M_defaultContext; public: static await_resume_context current() { await_resume_context _Context; _Context._M_context = details::_ContextCallback::_CaptureCurrent(); return _Context; } static await_resume_context any() { return await_resume_context{}; } template class _CallbackChain { _Ty _M_awaitable; await_resume_context* _M_contextPtr; public: _CallbackChain(const _Ty& _Awaitable, await_resume_context* _ContextPtr) : _M_awaitable(_Awaitable), _M_contextPtr(_ContextPtr) {} bool await_ready() { return false; } template void await_suspend(_Handle _Hnd) { _STD experimental::awaitable_traits<_Ty>::invoke_await_suspend(_M_awaitable, [=] { if (_M_contextPtr->_M_context == details::_ContextCallback()) { _M_contextPtr->_M_defaultContext._CallInContext([=] { _Hnd(); }); } else { _M_contextPtr->_M_context._CallInContext([=] { _Hnd(); }); } }); } auto await_resume() { return _STD experimental::awaitable_traits<_Ty>::invoke_await_resume(_M_awaitable); } }; class _Callback { await_resume_context* _M_contextPtr; public: explicit _Callback(await_resume_context* _ContextPtr) : _M_contextPtr(_ContextPtr) {} bool await_ready() { return false; } template void await_suspend(_Handle _Hnd) { if (_M_contextPtr->_M_context == details::_ContextCallback()) { _M_contextPtr->_M_defaultContext._CallInContext([=] { _Hnd(); }); } else { _M_contextPtr->_M_context._CallInContext([=] { _Hnd(); }); } } void await_resume() {} }; template auto get_awaitable(_Ty _Awaitable) { return _CallbackChain<_STD decay_t<_Ty>>(_Awaitable, this); } auto get_awaitable() { return _Callback(this); } }; } // namespace Concurrency #ifdef __cplusplus_winrt // WinRT IAsync(Operation|Action) awaitable extension namespace Windows { namespace Foundation { inline bool await_ready(IAsyncInfo ^ _Task) { return _Task->Status >= AsyncStatus::Completed; } template void await_suspend(IAsyncAction ^ _Task, _Handle _ResumeCb) { _Task->Completed = ref new AsyncActionCompletedHandler( [_ResumeCb](IAsyncAction ^, AsyncStatus) { _ResumeCb(); }, ::Platform::CallbackContext::Same); } template void await_suspend(IAsyncOperation<_Ty> ^ _Task, _Handle _ResumeCb) { _Task->Completed = ref new AsyncOperationCompletedHandler<_Ty>( [_ResumeCb](IAsyncOperation<_Ty> ^, AsyncStatus) { _ResumeCb(); }, ::Platform::CallbackContext::Same); } template void await_suspend(IAsyncActionWithProgress<_Pr> ^ _Task, _Handle _ResumeCb) { _Task->Completed = ref new AsyncActionWithProgressCompletedHandler<_Pr>( [_ResumeCb](IAsyncActionWithProgress<_Pr> ^, AsyncStatus) { _ResumeCb(); }, ::Platform::CallbackContext::Same); } template void await_suspend(IAsyncOperationWithProgress<_Ty, _Pr> ^ _Task, _Handle _ResumeCb) { _Task->Completed = ref new AsyncOperationWithProgressCompletedHandler<_Ty, _Pr>( [_ResumeCb](IAsyncOperationWithProgress<_Ty, _Pr> ^, AsyncStatus) { _ResumeCb(); }, ::Platform::CallbackContext::Same); } inline void await_resume(Windows::Foundation::IAsyncAction ^ _Task) { _VerifyStateForResultsCall(_Task->Status); _Task->GetResults(); } template _Ty await_resume(Windows::Foundation::IAsyncOperation<_Ty> ^ _Task) { _VerifyStateForResultsCall(_Task->Status); return _Task->GetResults(); } template void await_resume(Windows::Foundation::IAsyncActionWithProgress<_Pr> ^ _Task) { _VerifyStateForResultsCall(_Task->Status); _Task->GetResults(); } template _Ty await_resume(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Pr> ^ _Task) { _VerifyStateForResultsCall(_Task->Status); return _Task->GetResults(); } } // namespace Foundation } // namespace Windows #endif // __cplusplus_winrt #endif // _RESUMABLE_ENABLE_LEGACY_AWAIT_ADAPTERS #endif // _PPLAWAIT_H