From 82af7ded194f3a0c2e56ce3c92ba2266f9123af7 Mon Sep 17 00:00:00 2001 From: ioannis Date: Tue, 2 Oct 2018 01:36:55 +0300 Subject: [PATCH 1/2] Allow ppltasks.h and pplxtasks.h to co-exist --- Release/include/pplx/pplxtasks.h | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Release/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h index f7c3f58346..bf2187264a 100644 --- a/Release/include/pplx/pplxtasks.h +++ b/Release/include/pplx/pplxtasks.h @@ -136,13 +136,17 @@ namespace std } } #endif /* _MSC_VER < 1700 */ -#ifndef _PPLTASK_ASYNC_LOGGING - #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) - #define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt - #else - #define _PPLTASK_ASYNC_LOGGING 0 - #endif -#endif /* !_PPLTASK_ASYNC_LOGGING */ + +#ifndef _PPLTASK_ASYNC_LOGGING +#define _PPLTASK_ASYNC_LOGGING 1 +#endif // #ifndef _PPLTASK_ASYNC_LOGGING + +#if _PPLTASK_ASYNC_LOGGING +#pragma detect_mismatch("_PPLTASK_ASYNC_LOGGING", "1") +#else +#pragma detect_mismatch("_PPLTASK_ASYNC_LOGGING", "0") +#endif // #if _PPLTASK_ASYNC_LOGGING + #endif /* _MSC_VER */ #pragma pack(push,_CRT_PACKING) @@ -1460,7 +1464,7 @@ namespace details virtual ~_ContinuationTaskHandleBase() {} }; -#if _PPLTASK_ASYNC_LOGGING +#if _PPLTASK_ASYNC_LOGGING && _MSC_VER >= 1800 && defined(__cplusplus_winrt) // GUID used for identifying causality logs from PPLTask const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); @@ -2379,7 +2383,7 @@ namespace details _Task_impl_base const & operator=(_Task_impl_base const&); }; -#if _PPLTASK_ASYNC_LOGGING +#if _PPLTASK_ASYNC_LOGGING && _MSC_VER >= 1800 && defined(__cplusplus_winrt) inline void _TaskEventLogger::_LogTaskCompleted() { if (_M_scheduled) From 2310627c907999b38cb2e5ab80e09102f4dccf9d Mon Sep 17 00:00:00 2001 From: ioannis Date: Thu, 1 Nov 2018 12:30:31 +0200 Subject: [PATCH 2/2] Rename macros to avoid conflict 1. Rename _PPLTASKS_H to PPLXTASKS_H 2. Rename _PPLTASKS_WITH_WINRT to PPLXTASKS_WITH_WINRT 3. Rename _PPLTASK_ASYNC_LOGGING to PPLX_TASK_ASYNC_LOGGING 4. Rename _PPL_TASK_SAVE_FRAME_COUNT to PPLX_TASK_SAVE_FRAME_COUNT 5. Rename _CAPTURE_CALLSTACK to PPLX_CAPTURE_CALLSTACK --- Release/include/pplx/pplxtasks.h | 14708 ++++++++++++++--------------- 1 file changed, 7352 insertions(+), 7356 deletions(-) diff --git a/Release/include/pplx/pplxtasks.h b/Release/include/pplx/pplxtasks.h index bf2187264a..ab059f0c5a 100644 --- a/Release/include/pplx/pplxtasks.h +++ b/Release/include/pplx/pplxtasks.h @@ -1,7357 +1,7353 @@ -/*** -* Copyright (C) Microsoft. All rights reserved. -* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -* -* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -* -* Parallel Patterns Library - PPLx Tasks -* -* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk -* -* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -****/ - -#pragma once - -#ifndef _PPLXTASKS_H -#define _PPLXTASKS_H - -#include "cpprest/details/cpprest_compat.h" - -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX -#include -namespace pplx = Concurrency; - -namespace Concurrency -{ - -/// -/// Sets the ambient scheduler to be used by the PPL constructs. -/// -_ASYNCRTIMP void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr& _Scheduler); - -/// -/// Gets the ambient scheduler to be used by the PPL constructs -/// -_ASYNCRTIMP const std::shared_ptr& __cdecl get_cpprestsdk_ambient_scheduler(); - -} // namespace Concurrency - -#if (_MSC_VER >= 1900) -#include -namespace Concurrency { - namespace extensibility { - typedef ::std::condition_variable condition_variable_t; - typedef ::std::mutex critical_section_t; - typedef ::std::unique_lock< ::std::mutex> scoped_critical_section_t; - - typedef ::Concurrency::event event_t; - typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; - typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; - typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; - - typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; - typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; - } -} -#endif // _MSC_VER >= 1900 -#else - -#include "pplx/pplx.h" - -#if defined(__ANDROID__) -#include -void cpprest_init(JavaVM*); -#endif - -// Cannot build using a compiler that is older than dev10 SP1 -#if defined(_MSC_VER) -#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ -#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks -#endif /*IFSTRIP=IGN*/ -#endif /* defined(_MSC_VER) */ - -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -#include -#if defined(__cplusplus_winrt) -#include -#include -#include -#include -#ifndef _UITHREADCTXT_SUPPORT - -#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ - -// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user -#include - -#if WINAPI_FAMILY == WINAPI_FAMILY_APP - // UI thread context support is not required for desktop and Windows Store apps - #define _UITHREADCTXT_SUPPORT 0 -#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP - // UI thread context support is not required for desktop and Windows Store apps - #define _UITHREADCTXT_SUPPORT 0 -#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ - #define _UITHREADCTXT_SUPPORT 1 -#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ - -#else /* WINAPI_FAMILY */ - // Not supported without a WINAPI_FAMILY setting. - #define _UITHREADCTXT_SUPPORT 0 -#endif /* WINAPI_FAMILY */ - -#endif /* _UITHREADCTXT_SUPPORT */ - -#if _UITHREADCTXT_SUPPORT - #include -#endif /* _UITHREADCTXT_SUPPORT */ - - #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "1") -#else /* defined(__cplusplus_winrt) */ - #pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") -#endif /* defined(__cplusplus_winrt) */ -#endif /* defined(_MSC_VER) */ - -#ifdef _DEBUG - #define _DBG_ONLY(X) X -#else - #define _DBG_ONLY(X) -#endif // #ifdef _DEBUG - -// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. -#ifdef _MSC_VER -#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ -namespace std -{ - template exception_ptr make_exception_ptr(_E _Except) - { - return copy_exception(_Except); - } -} -#endif /* _MSC_VER < 1700 */ - -#ifndef _PPLTASK_ASYNC_LOGGING -#define _PPLTASK_ASYNC_LOGGING 1 -#endif // #ifndef _PPLTASK_ASYNC_LOGGING - -#if _PPLTASK_ASYNC_LOGGING -#pragma detect_mismatch("_PPLTASK_ASYNC_LOGGING", "1") +/*** +* Copyright (C) Microsoft. All rights reserved. +* Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +* +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* Parallel Patterns Library - PPLx Tasks +* +* For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef PPLXTASKS_H +#define PPLXTASKS_H + +#include "cpprest/details/cpprest_compat.h" + +#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX +#include +namespace pplx = Concurrency; + +namespace Concurrency +{ + +/// +/// Sets the ambient scheduler to be used by the PPL constructs. +/// +_ASYNCRTIMP void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr& _Scheduler); + +/// +/// Gets the ambient scheduler to be used by the PPL constructs +/// +_ASYNCRTIMP const std::shared_ptr& __cdecl get_cpprestsdk_ambient_scheduler(); + +} // namespace Concurrency + +#if (_MSC_VER >= 1900) +#include +namespace Concurrency { + namespace extensibility { + typedef ::std::condition_variable condition_variable_t; + typedef ::std::mutex critical_section_t; + typedef ::std::unique_lock< ::std::mutex> scoped_critical_section_t; + + typedef ::Concurrency::event event_t; + typedef ::Concurrency::reader_writer_lock reader_writer_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t; + typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t; + + typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t; + typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t; + } +} +#endif // _MSC_VER >= 1900 +#else + +#include "pplx/pplx.h" + +#if defined(__ANDROID__) +#include +void cpprest_init(JavaVM*); +#endif + +// Cannot build using a compiler that is older than dev10 SP1 +#if defined(_MSC_VER) +#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ +#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks +#endif /*IFSTRIP=IGN*/ +#endif /* defined(_MSC_VER) */ + +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#include +#if defined(__cplusplus_winrt) +#include +#include +#include +#include +#ifndef _UITHREADCTXT_SUPPORT + +#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ + +// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + #define _UITHREADCTXT_SUPPORT 1 +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + +#else /* WINAPI_FAMILY */ + // Not supported without a WINAPI_FAMILY setting. + #define _UITHREADCTXT_SUPPORT 0 +#endif /* WINAPI_FAMILY */ + +#endif /* _UITHREADCTXT_SUPPORT */ + +#if _UITHREADCTXT_SUPPORT + #include +#endif /* _UITHREADCTXT_SUPPORT */ + + #pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "1") +#else /* defined(__cplusplus_winrt) */ + #pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "0") +#endif /* defined(__cplusplus_winrt) */ +#endif /* defined(_MSC_VER) */ + +#ifdef _DEBUG + #define _DBG_ONLY(X) X +#else + #define _DBG_ONLY(X) +#endif // #ifdef _DEBUG + +// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. +#ifdef _MSC_VER +#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ +namespace std +{ + template exception_ptr make_exception_ptr(_E _Except) + { + return copy_exception(_Except); + } +} +#endif /* _MSC_VER < 1700 */ +#ifndef PPLX_TASK_ASYNC_LOGGING + #if _MSC_VER >= 1800 && defined(__cplusplus_winrt) + #define PPLX_TASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt + #else + #define PPLX_TASK_ASYNC_LOGGING 0 + #endif +#endif /* !PPLX_TASK_ASYNC_LOGGING */ +#endif /* _MSC_VER */ + +#pragma pack(push,_CRT_PACKING) + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 28197) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming +#endif /* defined(_MSC_VER) */ + +// All CRT public header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +// stuff ported from Dev11 CRT +// NOTE: this doesn't actually match std::declval. it behaves differently for void! +// so don't blindly change it to std::declval. +namespace stdx +{ + template + _T&& declval(); +} + +/// +/// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see . +/// +/**/ +namespace pplx +{ +/// +/// A type that represents the terminal state of a task. Valid values are completed and canceled. +/// +/// +/**/ +typedef task_group_status task_status; + +template class task; +template <> class task; + +// In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. +#ifndef PPLX_TASK_SAVE_FRAME_COUNT +#ifdef _DEBUG +#define PPLX_TASK_SAVE_FRAME_COUNT 10 +#else +#define PPLX_TASK_SAVE_FRAME_COUNT 1 +#endif +#endif + +/// +/// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, +/// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. +/// +/// +/// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() +/// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. +/// +#if PPLX_TASK_SAVE_FRAME_COUNT > 1 +#if defined(__cplusplus_winrt) && !defined(_DEBUG) +#pragma message ("WARNING: Redefining PPLX_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") +#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#else +#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPLX_TASK_SAVE_FRAME_COUNT) +#endif +#else +#define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#endif + + +/// +/// Returns an indication of whether the task that is currently executing has received a request to cancel its +/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and +/// the token source associated with that token is canceled. +/// +/// +/// true if the currently executing task has received a request for cancellation, false otherwise. +/// +/// +/// If you call this method in the body of a task and it returns true, you must respond with a call to +/// cancel_current_task to acknowledge the cancellation request, +/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into +/// the canceled state. If you do not respond and continue execution, or return instead of calling +/// cancel_current_task, the task will enter the completed state when it is done. +/// state. +/// A task is not cancelable if it was created without a cancellation token. +/// +/// +/// +/// +/// +/**/ +inline bool _pplx_cdecl is_task_cancellation_requested() +{ + return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested(); +} + +/// +/// Cancels the currently executing task. This function can be called from within the body of a task to abort the +/// task's execution and cause it to enter the canceled state. While it may be used in response to +/// the is_task_cancellation_requested function, you may +/// also use it by itself, to initiate cancellation of the task that is currently executing. +/// It is not a supported scenario to call this function if you are not within the body of a task. +/// Doing so will result in undefined behavior such as a crash or a hang in your application. +/// +/// +/**/ +inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() +{ + throw task_canceled(); +} + +namespace details +{ + /// + /// Callstack container, which is used to capture and preserve callstacks in ppltasks. + /// Members of this class is examined by vc debugger, thus there will be no public access methods. + /// Please note that names of this class should be kept stable for debugger examining. + /// + class _TaskCreationCallstack + { + private: + // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; + // otherwise, _M_Frame will store all the callstack frames. + void* _M_SingleFrame; + std::vector _M_frames; + public: + _TaskCreationCallstack() + { + _M_SingleFrame = nullptr; + } + + // Store one frame of callstack. This function works for both Debug / Release CRT. + static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) + { + _TaskCreationCallstack _csc; + _csc._M_SingleFrame = _SingleFrame; + return _csc; + } + + // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. + __declspec(noinline) + static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) + { + _TaskCreationCallstack _csc; + _csc._M_frames.resize(_CaptureFrames); + // skip 2 frames to make sure callstack starts from user code + _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); + return _csc; + } + }; + typedef unsigned char _Unit_type; + + struct _TypeSelectorNoAsync {}; + struct _TypeSelectorAsyncOperationOrTask {}; + struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncAction {}; + struct _TypeSelectorAsyncActionWithProgress {}; + struct _TypeSelectorAsyncOperationWithProgress {}; + + template + struct _NormalizeVoidToUnitType + { + typedef _Ty _Type; + }; + + template<> + struct _NormalizeVoidToUnitType + { + typedef _Unit_type _Type; + }; + + template + struct _IsUnwrappedAsyncSelector + { + static const bool _Value = true; + }; + + template<> + struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> + { + static const bool _Value = false; + }; + + template + struct _UnwrapTaskType + { + typedef _Ty _Type; + }; + + template + struct _UnwrapTaskType> + { + typedef _Ty _Type; + }; + + template + _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); + + _TypeSelectorNoAsync _AsyncOperationKindSelector(...); + +#if defined(__cplusplus_winrt) + template + struct _Unhat + { + typedef _Type _Value; + }; + + template + struct _Unhat<_Type^> + { + typedef _Type _Value; + }; + + value struct _NonUserType { public: int _Dummy; }; + + template + struct _ValueTypeOrRefType + { + typedef _NonUserType _Value; + }; + + template + struct _ValueTypeOrRefType<_Type, true> + { + typedef _Type _Value; + }; + + template + _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); + + template + _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); + + template + struct _GetProgressType + { + typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; + }; + + template + struct _IsIAsyncInfo + { + static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); + }; + + template + _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); + + _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); + + template + _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); + + template + _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); + + template ::_Value> + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = _IsAsync; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + + template + struct _TaskTypeTraits<_Type, true > + { + typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; + typedef _TaskRetType _NormalizedTaskRetType; + typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; + + static const bool _IsAsyncTask = true; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + +#else /* defined (__cplusplus_winrt) */ + template + struct _IsIAsyncInfo + { + static const bool _Value = false; + }; + + template + struct _TaskTypeTraits + { + typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; +#endif /* defined (__cplusplus_winrt) */ + + template auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (void)(_Func); return std::true_type(); } + template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } + + template <> + struct _TaskTypeTraits + { + typedef void _TaskRetType; + typedef _TypeSelectorNoAsync _AsyncKind; + typedef _Unit_type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + template + task<_Type> _To_task(_Type t); + + template + task _To_task_void(_Func f); + + struct _BadContinuationParamType{}; + + template auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); + template auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); + template auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; + + template auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); + template std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); + + template auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func))); + template auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); + + template auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type()); + template std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); + + template + struct _FunctionTypeTraits + { + typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; + static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template + struct _FunctionTypeTraits<_Function, void> + { + typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; + typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; + }; + + template + struct _ContinuationTypeTraits + { + typedef task::_FuncRetType>::_TaskRetType> _TaskOfType; + }; + + // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is + // declared, the constructor may or may not perform unwrapping. For eg. + // + // This declaration SHOULD NOT cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // + // This declaration SHOULD cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. + template + struct _InitFunctorTypeTraits + { + typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; + static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; + static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; + }; + + template + struct _InitFunctorTypeTraits + { + typedef _TypeSelectorNoAsync _AsyncKind; + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + /// + /// Helper object used for LWT invocation. + /// + struct _TaskProcThunk + { + _TaskProcThunk(const std::function & _Callback) : + _M_func(_Callback) + { + } + + static void _pplx_cdecl _Bridge(void *_PData) + { + _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); + _Holder _ThunkHolder(_PThunk); + _PThunk->_M_func(); + } + private: + + // RAII holder + struct _Holder + { + _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) + { + } + + ~_Holder() + { + delete _M_pThunk; + } + + _TaskProcThunk * _M_pThunk; + + private: + _Holder& operator=(const _Holder&); + }; + + std::function _M_func; + _TaskProcThunk& operator=(const _TaskProcThunk&); + }; + + /// + /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be + /// waited on or canceled after scheduling. + /// This schedule method will perform automatic inlining base on . + /// + /// + /// The user functor need to be scheduled. + /// + /// + /// The inlining scheduling policy for current functor. + /// + static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode_t _InliningMode) + { + _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); + } + + class _ContextCallback + { + typedef std::function _CallbackFunction; + +#if defined (__cplusplus_winrt) + + public: + + static _ContextCallback _CaptureCurrent() + { + _ContextCallback _Context; + _Context._Capture(); + return _Context; + } + + ~_ContextCallback() + { + _Reset(); + } + + _ContextCallback(bool _DeferCapture = false) + { + if (_DeferCapture) + { + _M_context._M_captureMethod = _S_captureDeferred; + } + else + { + _M_context._M_pContextCallback = nullptr; + } + } + + // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). + void _Resolve(bool _CaptureCurrent) + { + if(_M_context._M_captureMethod == _S_captureDeferred) + { + _M_context._M_pContextCallback = nullptr; + + if (_CaptureCurrent) + { + if (_IsCurrentOriginSTA()) + { + _Capture(); + } +#if _UITHREADCTXT_SUPPORT + else + { + // This method will fail if not called from the UI thread. + HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } +#endif /* _UITHREADCTXT_SUPPORT */ + } + } + } + + void _Capture() + { + HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } + + _ContextCallback(const _ContextCallback& _Src) + { + _Assign(_Src._M_context._M_pContextCallback); + } + + _ContextCallback(_ContextCallback&& _Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + + _ContextCallback& operator=(const _ContextCallback& _Src) + { + if (this != &_Src) + { + _Reset(); + _Assign(_Src._M_context._M_pContextCallback); + } + return *this; + } + + _ContextCallback& operator=(_ContextCallback&& _Src) + { + if (this != &_Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + return *this; + } + + bool _HasCapturedContext() const + { + _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); + return (_M_context._M_pContextCallback != nullptr); + } + + void _CallInContext(_CallbackFunction _Func) const + { + if (!_HasCapturedContext()) + { + _Func(); + } + else + { + ComCallData callData; + ZeroMemory(&callData, sizeof(callData)); + callData.pUserDefined = reinterpret_cast(&_Func); + + HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); + if (FAILED(_Hr)) + { + throw ::Platform::Exception::CreateException(_Hr); + } + } + } + + bool operator==(const _ContextCallback& _Rhs) const + { + return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); + } + + bool operator!=(const _ContextCallback& _Rhs) const + { + return !(operator==(_Rhs)); + } + + private: + void _Reset() + { + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->Release(); + } + } + + void _Assign(IContextCallback *_PContextCallback) + { + _M_context._M_pContextCallback = _PContextCallback; + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->AddRef(); + } + } + + static HRESULT __stdcall _Bridge(ComCallData *_PParam) + { + _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); + (*pFunc)(); + return S_OK; + } + + // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) + static bool _IsCurrentOriginSTA() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + if (SUCCEEDED(hr)) + { + // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether + // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in + // a neutral apartment when it schedules a continuation, we will not marshal continuations back to the STA, + // since variables used within a neutral apartment are expected to be apartment neutral. + switch(_AptType) + { + case APTTYPE_MAINSTA: + case APTTYPE_STA: + return true; + default: + break; + } + } + return false; + } + + union + { + IContextCallback *_M_pContextCallback; + size_t _M_captureMethod; + } _M_context; + + static const size_t _S_captureDeferred = 1; +#else /* defined (__cplusplus_winrt) */ + public: + + static _ContextCallback _CaptureCurrent() + { + return _ContextCallback(); + } + + _ContextCallback(bool = false) + { + } + + _ContextCallback(const _ContextCallback&) + { + } + + _ContextCallback(_ContextCallback&&) + { + } + + _ContextCallback& operator=(const _ContextCallback&) + { + return *this; + } + + _ContextCallback& operator=(_ContextCallback&&) + { + return *this; + } + + bool _HasCapturedContext() const + { + return false; + } + + void _Resolve(bool) const + { + } + + void _CallInContext(_CallbackFunction _Func) const + { + _Func(); + } + + bool operator==(const _ContextCallback&) const + { + return true; + } + + bool operator!=(const _ContextCallback&) const + { + return false; + } + +#endif /* defined (__cplusplus_winrt) */ + }; + + template + struct _ResultHolder + { + void Set(const _Type& _type) + { + _Result = _type; + } + + _Type Get() + { + return _Result; + } + + _Type _Result; + }; + +#if defined (__cplusplus_winrt) + + template + struct _ResultHolder<_Type^> + { + void Set(_Type^ const & _type) + { + _M_Result = _type; + } + + _Type^ Get() + { + return _M_Result.Get(); + } + private: + // ::Platform::Agile handle specialization of all hats + // including ::Platform::String and ::Platform::Array + ::Platform::Agile<_Type^> _M_Result; + }; + + // + // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. + // + template + struct _ResultHolder> + { + void Set(const std::vector<_Type^>& _type) + { + _Result.reserve(_type.size()); + + for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) + { + _Result.emplace_back(*_PTask); + } + } + + std::vector<_Type^> Get() + { + // Return vectory with the objects that are marshaled in the proper apartment + std::vector<_Type^> _Return; + _Return.reserve(_Result.size()); + + for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) + { + _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate apartment if necessary + } + + return _Return; + } + + std::vector< ::Platform::Agile<_Type^> > _Result; + }; + + template + struct _ResultHolder > + { + void Set(const std::pair<_Type^, size_t>& _type) + { + _M_Result = _type; + } + + std::pair<_Type^, size_t> Get() + { + return std::make_pair(_M_Result.first.Get(), _M_Result.second); + } + private: + std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; + }; + +#endif /* defined (__cplusplus_winrt) */ + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + struct _ExceptionHolder + { + private: + void ReportUnhandledError() + { +#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + ::Platform::Details::ReportUnhandledError(_M_winRTException); + } +#endif /* defined (__cplusplus_winrt) */ + } + public: + explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) +#if defined (__cplusplus_winrt) + , _M_winRTException(nullptr) +#endif /* defined (__cplusplus_winrt) */ + { + } + +#if defined (__cplusplus_winrt) + explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) + { + } +#endif /* defined (__cplusplus_winrt) */ + + __declspec(noinline) + ~_ExceptionHolder() + { + if (_M_exceptionObserved == 0) + { + // If you are trapped here, it means an exception thrown in task chain didn't get handled. + // Please add task-based continuation to handle all exceptions coming from tasks. + // this->_M_stackTrace keeps the creation callstack of the task generates this exception. + _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); + } + } + + void _RethrowUserException() + { + if (_M_exceptionObserved == 0) + { + atomic_exchange(_M_exceptionObserved, 1l); + } + +#if defined (__cplusplus_winrt) + if (_M_winRTException != nullptr) + { + throw _M_winRTException; + } +#endif /* defined (__cplusplus_winrt) */ + std::rethrow_exception(_M_stdException); + } + + // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that + // are unobserved when the exception holder is destructed will terminate the process. + atomic_long _M_exceptionObserved; + + // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. + std::exception_ptr _M_stdException; +#if defined (__cplusplus_winrt) + ::Platform::Exception^ _M_winRTException; +#endif /* defined (__cplusplus_winrt) */ + + // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, + // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call + // is to task_completion_event::set_exception, the set_exception method was the source of the exception. + // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. + _TaskCreationCallstack _M_stackTrace; + + }; + +#if defined (__cplusplus_winrt) + /// + /// Base converter class for converting asynchronous interfaces to IAsyncOperation + /// + template + ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> + { + internal: + // The async action, action with progress or operation with progress that this stub forwards to. + ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; + + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; + + _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} + + public: + virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } + virtual void Close() { _M_asyncInfo.Get()->Close(); } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + return _M_asyncInfo.Get()->ErrorCode; + } + } + + virtual property UINT Id + { + UINT get() + { + return _M_asyncInfo.Get()->Id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + return _M_asyncInfo.Get()->Status; + } + } + + virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } + + virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed + { + Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() + { + return _M_CompletedHandler; + } + + void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) + { + _M_CompletedHandler = value; + _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { + _M_CompletedHandler->Invoke(this, status); + }); + } + } + }; + + /// + /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation + /// + template + ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result> + { + internal: + _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : + _AsyncInfoImpl^, + Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, + _Result>(_Operation) {} + + public: + virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } + }; + + /// + /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> + /// + ref struct _IAsyncActionToAsyncOperationConverter sealed : + _AsyncInfoImpl + { + internal: + _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : + _AsyncInfoImpl(_Operation) {} + + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; + + /// + /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> + /// + template + ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : + _AsyncInfoImpl^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type> + { + internal: + _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : + _AsyncInfoImpl^, + Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, + details::_Unit_type>(_Action) {} + public: + virtual details::_Unit_type GetResults() override + { + // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. + _M_asyncInfo.Get()->GetResults(); + return details::_Unit_type(); + } + }; +#endif /* defined (__cplusplus_winrt) */ +} // namespace details + +/// +/// The task_continuation_context class allows you to specify where you would like a continuation to be executed. +/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's +/// execution context is determined by the runtime, and not configurable. +/// +/// +/**/ +class task_continuation_context : public details::_ContextCallback +{ +public: + + /// + /// Creates the default task continuation context. + /// + /// + /// The default continuation context. + /// + /// + /// The default context is used if you don't specify a continuation context when you call the then method. In Windows + /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where + /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an + /// apartment aware task is the apartment where then is invoked. + /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such + /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in + /// that STA. + /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. + /// + /**/ + static task_continuation_context use_default() + { +#if defined (__cplusplus_winrt) + // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() + return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle +#else /* defined (__cplusplus_winrt) */ + return task_continuation_context(); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + /// + /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. + /// + /// + /// A task continuation context that represents an arbitrary location. + /// + /// + /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task + /// is apartment aware. + /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment + /// aware task created in an STA. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_arbitrary() + { + task_continuation_context _Arbitrary(true); + _Arbitrary._Resolve(false); + return _Arbitrary; + } + + /// + /// Returns a task continuation context object that represents the current execution context. + /// + /// + /// The current execution context. + /// + /// + /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. + /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in + /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is + /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_current() + { + task_continuation_context _Current(true); + _Current._Resolve(true); + return _Current; + } +#endif /* defined (__cplusplus_winrt) */ + +private: + + task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) + { + } +}; + +class task_options; +namespace details +{ + struct _Internal_task_options + { + bool _M_hasPresetCreationCallstack; + _TaskCreationCallstack _M_presetCreationCallstack; + + void _set_creation_callstack(const _TaskCreationCallstack &_callstack) + { + _M_hasPresetCreationCallstack = true; + _M_presetCreationCallstack = _callstack; + } + _Internal_task_options() + { + _M_hasPresetCreationCallstack = false; + } + }; + + inline _Internal_task_options &_get_internal_task_options(task_options &options); + inline const _Internal_task_options &_get_internal_task_options(const task_options &options); +} +/// +/// Represents the allowed options for creating a task +/// +class task_options +{ +public: + + + /// + /// Default list of task creation options + /// + task_options() + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token + /// + task_options(cancellation_token _Token) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(true), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a continuation context. This is valid only for continuations (then) + /// + task_options(task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) + /// + task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) + : _M_Scheduler(get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a scheduler with shared lifetime + /// + template + task_options(std::shared_ptr<_SchedType> _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler reference + /// + task_options(scheduler_interface& _Scheduler) + : _M_Scheduler(&_Scheduler), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler + /// + task_options(scheduler_ptr _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option copy constructor + /// + task_options(const task_options& _TaskOptions) + : _M_Scheduler(_TaskOptions.get_scheduler()), + _M_CancellationToken(_TaskOptions.get_cancellation_token()), + _M_ContinuationContext(_TaskOptions.get_continuation_context()), + _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), + _M_HasScheduler(_TaskOptions.has_scheduler()) + { + } + + /// + /// Sets the given token in the options + /// + void set_cancellation_token(cancellation_token _Token) + { + _M_CancellationToken = _Token; + _M_HasCancellationToken = true; + } + + /// + /// Sets the given continuation context in the options + /// + void set_continuation_context(task_continuation_context _ContinuationContext) + { + _M_ContinuationContext = _ContinuationContext; + } + + /// + /// Indicates whether a cancellation token was specified by the user + /// + bool has_cancellation_token() const + { + return _M_HasCancellationToken; + } + + /// + /// Returns the cancellation token + /// + cancellation_token get_cancellation_token() const + { + return _M_CancellationToken; + } + + /// + /// Returns the continuation context + /// + task_continuation_context get_continuation_context() const + { + return _M_ContinuationContext; + } + + /// + /// Indicates whether a scheduler n was specified by the user + /// + bool has_scheduler() const + { + return _M_HasScheduler; + } + + /// + /// Returns the scheduler + /// + scheduler_ptr get_scheduler() const + { + return _M_Scheduler; + } + +private: + + task_options const& operator=(task_options const& _Right); + friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); + friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); + + scheduler_ptr _M_Scheduler; + cancellation_token _M_CancellationToken; + task_continuation_context _M_ContinuationContext; + details::_Internal_task_options _M_InternalTaskOptions; + bool _M_HasCancellationToken; + bool _M_HasScheduler; +}; + +namespace details +{ + inline _Internal_task_options & _get_internal_task_options(task_options &options) + { + return options._M_InternalTaskOptions; + } + inline const _Internal_task_options & _get_internal_task_options(const task_options &options) + { + return options._M_InternalTaskOptions; + } + + struct _Task_impl_base; + template struct _Task_impl; + + template + struct _Task_ptr + { + typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; + static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } + }; + + typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; + typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; + + // The weak-typed base task handler for continuation tasks. + struct _ContinuationTaskHandleBase : _UnrealizedChore_t + { + _ContinuationTaskHandleBase * _M_next; + task_continuation_context _M_continuationContext; + bool _M_isTaskBasedContinuation; + + // This field gives inlining scheduling policy for current chore. + _TaskInliningMode_t _M_inliningMode; + + virtual _Task_ptr_base _GetTaskImplBase() const = 0; + + _ContinuationTaskHandleBase() : + _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) + { + } + + virtual ~_ContinuationTaskHandleBase() {} + }; + +#if PPLX_TASK_ASYNC_LOGGING + // GUID used for identifying causality logs from PPLTask + const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); + + __declspec(selectany) volatile long _isCausalitySupported = 0; + + inline bool _IsCausalitySupported() + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (_isCausalitySupported == 0) + { + long _causality = 1; + OSVERSIONINFOEX _osvi = {}; + _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + // The Causality is supported on Windows version higher than Windows 8 + _osvi.dwMajorVersion = 6; + _osvi.dwMinorVersion = 3; + + DWORDLONG _conditionMask = 0; + VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); + VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); + + if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) + { + _causality = 2; + } + + _isCausalitySupported = _causality; + return _causality == 2; + } + + return _isCausalitySupported == 2 ? true : false; +#else + return true; +#endif + } + + // Stateful logger rests inside task_impl_base. + struct _TaskEventLogger + { + _Task_impl_base *_M_task; + bool _M_scheduled; + bool _M_taskPostEventStarted; + + // Log before scheduling task + void _LogScheduleTask(bool _isContinuation) + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), + _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask", 0); + _M_scheduled = true; + } + } + + // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. + void _LogCancelTask() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); + + } + } + + // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run + void _LogTaskCompleted(); + + // Log when task body (which includes user lambda and other scheduling code) begin to run + void _LogTaskExecutionStarted() { } + + // Log when task body finish executing + void _LogTaskExecutionCompleted() + { + if (_M_taskPostEventStarted && details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + } + } + + // Log right before user lambda being invoked + void _LogWorkItemStarted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + } + } + + // Log right after user lambda being invoked + void _LogWorkItemCompleted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + _M_taskPostEventStarted = true; + } + } + + _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) + { + _M_scheduled = false; + _M_taskPostEventStarted = false; + } + }; + + // Exception safe logger for user lambda + struct _TaskWorkItemRAIILogger + { + _TaskEventLogger &_M_logger; + _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) + { + _M_logger._LogWorkItemStarted(); + } + + ~_TaskWorkItemRAIILogger() + { + _M_logger._LogWorkItemCompleted(); + } + _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned + }; + #else -#pragma detect_mismatch("_PPLTASK_ASYNC_LOGGING", "0") -#endif // #if _PPLTASK_ASYNC_LOGGING - -#endif /* _MSC_VER */ - -#pragma pack(push,_CRT_PACKING) - -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 28197) -#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation -#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming -#endif /* defined(_MSC_VER) */ - -// All CRT public header files are required to be protected from the macro new -#pragma push_macro("new") -#undef new - -// stuff ported from Dev11 CRT -// NOTE: this doesn't actually match std::declval. it behaves differently for void! -// so don't blindly change it to std::declval. -namespace stdx -{ - template - _T&& declval(); -} - -/// -/// The pplx namespace provides classes and functions that give you access to the Concurrency Runtime, -/// a concurrent programming framework for C++. For more information, see . -/// -/**/ -namespace pplx -{ -/// -/// A type that represents the terminal state of a task. Valid values are completed and canceled. -/// -/// -/**/ -typedef task_group_status task_status; - -template class task; -template <> class task; - -// In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. -#ifndef PPL_TASK_SAVE_FRAME_COUNT -#ifdef _DEBUG -#define PPL_TASK_SAVE_FRAME_COUNT 10 -#else -#define PPL_TASK_SAVE_FRAME_COUNT 1 -#endif -#endif - -/// -/// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, -/// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. -/// -/// -/// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() -/// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. -/// -#if PPL_TASK_SAVE_FRAME_COUNT > 1 -#if defined(__cplusplus_winrt) && !defined(_DEBUG) -#pragma message ("WARNING: Redefining PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") -#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) -#else -#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) -#endif -#else -#define _CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) -#endif - - -/// -/// Returns an indication of whether the task that is currently executing has received a request to cancel its -/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and -/// the token source associated with that token is canceled. -/// -/// -/// true if the currently executing task has received a request for cancellation, false otherwise. -/// -/// -/// If you call this method in the body of a task and it returns true, you must respond with a call to -/// cancel_current_task to acknowledge the cancellation request, -/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into -/// the canceled state. If you do not respond and continue execution, or return instead of calling -/// cancel_current_task, the task will enter the completed state when it is done. -/// state. -/// A task is not cancelable if it was created without a cancellation token. -/// -/// -/// -/// -/// -/**/ -inline bool _pplx_cdecl is_task_cancellation_requested() -{ - return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested(); -} - -/// -/// Cancels the currently executing task. This function can be called from within the body of a task to abort the -/// task's execution and cause it to enter the canceled state. While it may be used in response to -/// the is_task_cancellation_requested function, you may -/// also use it by itself, to initiate cancellation of the task that is currently executing. -/// It is not a supported scenario to call this function if you are not within the body of a task. -/// Doing so will result in undefined behavior such as a crash or a hang in your application. -/// -/// -/**/ -inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() -{ - throw task_canceled(); -} - -namespace details -{ - /// - /// Callstack container, which is used to capture and preserve callstacks in ppltasks. - /// Members of this class is examined by vc debugger, thus there will be no public access methods. - /// Please note that names of this class should be kept stable for debugger examining. - /// - class _TaskCreationCallstack - { - private: - // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; - // otherwise, _M_Frame will store all the callstack frames. - void* _M_SingleFrame; - std::vector _M_frames; - public: - _TaskCreationCallstack() - { - _M_SingleFrame = nullptr; - } - - // Store one frame of callstack. This function works for both Debug / Release CRT. - static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) - { - _TaskCreationCallstack _csc; - _csc._M_SingleFrame = _SingleFrame; - return _csc; - } - - // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. - __declspec(noinline) - static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) - { - _TaskCreationCallstack _csc; - _csc._M_frames.resize(_CaptureFrames); - // skip 2 frames to make sure callstack starts from user code - _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); - return _csc; - } - }; - typedef unsigned char _Unit_type; - - struct _TypeSelectorNoAsync {}; - struct _TypeSelectorAsyncOperationOrTask {}; - struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; - struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; - struct _TypeSelectorAsyncAction {}; - struct _TypeSelectorAsyncActionWithProgress {}; - struct _TypeSelectorAsyncOperationWithProgress {}; - - template - struct _NormalizeVoidToUnitType - { - typedef _Ty _Type; - }; - - template<> - struct _NormalizeVoidToUnitType - { - typedef _Unit_type _Type; - }; - - template - struct _IsUnwrappedAsyncSelector - { - static const bool _Value = true; - }; - - template<> - struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> - { - static const bool _Value = false; - }; - - template - struct _UnwrapTaskType - { - typedef _Ty _Type; - }; - - template - struct _UnwrapTaskType> - { - typedef _Ty _Type; - }; - - template - _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); - - _TypeSelectorNoAsync _AsyncOperationKindSelector(...); - -#if defined(__cplusplus_winrt) - template - struct _Unhat - { - typedef _Type _Value; - }; - - template - struct _Unhat<_Type^> - { - typedef _Type _Value; - }; - - value struct _NonUserType { public: int _Dummy; }; - - template - struct _ValueTypeOrRefType - { - typedef _NonUserType _Value; - }; - - template - struct _ValueTypeOrRefType<_Type, true> - { - typedef _Type _Value; - }; - - template - _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); - - template - _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); - - template - struct _GetProgressType - { - typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; - }; - - template - struct _IsIAsyncInfo - { - static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); - }; - - template - _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); - - _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); - - template - _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); - - template - _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); - - template ::_Value> - struct _TaskTypeTraits - { - typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; - typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; - typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; - - static const bool _IsAsyncTask = _IsAsync; - static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; - }; - - template - struct _TaskTypeTraits<_Type, true > - { - typedef decltype(((_Type)nullptr)->GetResults()) _TaskRetType; - typedef _TaskRetType _NormalizedTaskRetType; - typedef decltype(_AsyncOperationKindSelector((_Type)nullptr)) _AsyncKind; - - static const bool _IsAsyncTask = true; - static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; - }; - -#else /* defined (__cplusplus_winrt) */ - template - struct _IsIAsyncInfo - { - static const bool _Value = false; - }; - - template - struct _TaskTypeTraits - { - typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; - typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; - typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; - - static const bool _IsAsyncTask = false; - static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; - }; -#endif /* defined (__cplusplus_winrt) */ - - template auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { (void)(_Func); return std::true_type(); } - template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } - - template <> - struct _TaskTypeTraits - { - typedef void _TaskRetType; - typedef _TypeSelectorNoAsync _AsyncKind; - typedef _Unit_type _NormalizedTaskRetType; - - static const bool _IsAsyncTask = false; - static const bool _IsUnwrappedTaskOrAsync = false; - }; - - template - task<_Type> _To_task(_Type t); - - template - task _To_task_void(_Func f); - - struct _BadContinuationParamType{}; - - template auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t))); - template auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t)); - template auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType; - - template auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type()); - template std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); - - template auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func))); - template auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); - - template auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type()); - template std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); - - template - struct _FunctionTypeTraits - { - typedef decltype(_ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _FuncRetType; - static_assert(!std::is_same<_FuncRetType,_BadContinuationParamType>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); - - typedef decltype(_IsTaskHelper(stdx::declval<_ExpectedParameterType>(),stdx::declval<_Function>(), 0, 0)) _Takes_task; - }; - - template - struct _FunctionTypeTraits<_Function, void> - { - typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType; - typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task; - }; - - template - struct _ContinuationTypeTraits - { - typedef task::_FuncRetType>::_TaskRetType> _TaskOfType; - }; - - // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is - // declared, the constructor may or may not perform unwrapping. For eg. - // - // This declaration SHOULD NOT cause unwrapping - // task> t1([]() -> task { - // task t2([]() {}); - // return t2; - // }); - // - // This declaration SHOULD cause unwrapping - // task> t1([]() -> task { - // task t2([]() {}); - // return t2; - // }); - // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. - template - struct _InitFunctorTypeTraits - { - typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; - static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; - static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; - }; - - template - struct _InitFunctorTypeTraits - { - typedef _TypeSelectorNoAsync _AsyncKind; - static const bool _IsAsyncTask = false; - static const bool _IsUnwrappedTaskOrAsync = false; - }; - - /// - /// Helper object used for LWT invocation. - /// - struct _TaskProcThunk - { - _TaskProcThunk(const std::function & _Callback) : - _M_func(_Callback) - { - } - - static void _pplx_cdecl _Bridge(void *_PData) - { - _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); - _Holder _ThunkHolder(_PThunk); - _PThunk->_M_func(); - } - private: - - // RAII holder - struct _Holder - { - _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) - { - } - - ~_Holder() - { - delete _M_pThunk; - } - - _TaskProcThunk * _M_pThunk; - - private: - _Holder& operator=(const _Holder&); - }; - - std::function _M_func; - _TaskProcThunk& operator=(const _TaskProcThunk&); - }; - - /// - /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be - /// waited on or canceled after scheduling. - /// This schedule method will perform automatic inlining base on . - /// - /// - /// The user functor need to be scheduled. - /// - /// - /// The inlining scheduling policy for current functor. - /// - static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode_t _InliningMode) - { - _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); - } - - class _ContextCallback - { - typedef std::function _CallbackFunction; - -#if defined (__cplusplus_winrt) - - public: - - static _ContextCallback _CaptureCurrent() - { - _ContextCallback _Context; - _Context._Capture(); - return _Context; - } - - ~_ContextCallback() - { - _Reset(); - } - - _ContextCallback(bool _DeferCapture = false) - { - if (_DeferCapture) - { - _M_context._M_captureMethod = _S_captureDeferred; - } - else - { - _M_context._M_pContextCallback = nullptr; - } - } - - // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). - void _Resolve(bool _CaptureCurrent) - { - if(_M_context._M_captureMethod == _S_captureDeferred) - { - _M_context._M_pContextCallback = nullptr; - - if (_CaptureCurrent) - { - if (_IsCurrentOriginSTA()) - { - _Capture(); - } -#if _UITHREADCTXT_SUPPORT - else - { - // This method will fail if not called from the UI thread. - HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); - if (FAILED(_Hr)) - { - _M_context._M_pContextCallback = nullptr; - } - } -#endif /* _UITHREADCTXT_SUPPORT */ - } - } - } - - void _Capture() - { - HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); - if (FAILED(_Hr)) - { - _M_context._M_pContextCallback = nullptr; - } - } - - _ContextCallback(const _ContextCallback& _Src) - { - _Assign(_Src._M_context._M_pContextCallback); - } - - _ContextCallback(_ContextCallback&& _Src) - { - _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; - _Src._M_context._M_pContextCallback = nullptr; - } - - _ContextCallback& operator=(const _ContextCallback& _Src) - { - if (this != &_Src) - { - _Reset(); - _Assign(_Src._M_context._M_pContextCallback); - } - return *this; - } - - _ContextCallback& operator=(_ContextCallback&& _Src) - { - if (this != &_Src) - { - _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; - _Src._M_context._M_pContextCallback = nullptr; - } - return *this; - } - - bool _HasCapturedContext() const - { - _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred); - return (_M_context._M_pContextCallback != nullptr); - } - - void _CallInContext(_CallbackFunction _Func) const - { - if (!_HasCapturedContext()) - { - _Func(); - } - else - { - ComCallData callData; - ZeroMemory(&callData, sizeof(callData)); - callData.pUserDefined = reinterpret_cast(&_Func); - - HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); - if (FAILED(_Hr)) - { - throw ::Platform::Exception::CreateException(_Hr); - } - } - } - - bool operator==(const _ContextCallback& _Rhs) const - { - return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); - } - - bool operator!=(const _ContextCallback& _Rhs) const - { - return !(operator==(_Rhs)); - } - - private: - void _Reset() - { - if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) - { - _M_context._M_pContextCallback->Release(); - } - } - - void _Assign(IContextCallback *_PContextCallback) - { - _M_context._M_pContextCallback = _PContextCallback; - if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) - { - _M_context._M_pContextCallback->AddRef(); - } - } - - static HRESULT __stdcall _Bridge(ComCallData *_PParam) - { - _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); - (*pFunc)(); - return S_OK; - } - - // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) - static bool _IsCurrentOriginSTA() - { - APTTYPE _AptType; - APTTYPEQUALIFIER _AptTypeQualifier; - - HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); - if (SUCCEEDED(hr)) - { - // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether - // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in - // a neutral apartment when it schedules a continuation, we will not marshal continuations back to the STA, - // since variables used within a neutral apartment are expected to be apartment neutral. - switch(_AptType) - { - case APTTYPE_MAINSTA: - case APTTYPE_STA: - return true; - default: - break; - } - } - return false; - } - - union - { - IContextCallback *_M_pContextCallback; - size_t _M_captureMethod; - } _M_context; - - static const size_t _S_captureDeferred = 1; -#else /* defined (__cplusplus_winrt) */ - public: - - static _ContextCallback _CaptureCurrent() - { - return _ContextCallback(); - } - - _ContextCallback(bool = false) - { - } - - _ContextCallback(const _ContextCallback&) - { - } - - _ContextCallback(_ContextCallback&&) - { - } - - _ContextCallback& operator=(const _ContextCallback&) - { - return *this; - } - - _ContextCallback& operator=(_ContextCallback&&) - { - return *this; - } - - bool _HasCapturedContext() const - { - return false; - } - - void _Resolve(bool) const - { - } - - void _CallInContext(_CallbackFunction _Func) const - { - _Func(); - } - - bool operator==(const _ContextCallback&) const - { - return true; - } - - bool operator!=(const _ContextCallback&) const - { - return false; - } - -#endif /* defined (__cplusplus_winrt) */ - }; - - template - struct _ResultHolder - { - void Set(const _Type& _type) - { - _Result = _type; - } - - _Type Get() - { - return _Result; - } - - _Type _Result; - }; - -#if defined (__cplusplus_winrt) - - template - struct _ResultHolder<_Type^> - { - void Set(_Type^ const & _type) - { - _M_Result = _type; - } - - _Type^ Get() - { - return _M_Result.Get(); - } - private: - // ::Platform::Agile handle specialization of all hats - // including ::Platform::String and ::Platform::Array - ::Platform::Agile<_Type^> _M_Result; - }; - - // - // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. - // - template - struct _ResultHolder> - { - void Set(const std::vector<_Type^>& _type) - { - _Result.reserve(_type.size()); - - for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) - { - _Result.emplace_back(*_PTask); - } - } - - std::vector<_Type^> Get() - { - // Return vectory with the objects that are marshaled in the proper apartment - std::vector<_Type^> _Return; - _Return.reserve(_Result.size()); - - for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) - { - _Return.push_back(_PTask->Get()); // Platform::Agile will marshal the object to appropriate apartment if necessary - } - - return _Return; - } - - std::vector< ::Platform::Agile<_Type^> > _Result; - }; - - template - struct _ResultHolder > - { - void Set(const std::pair<_Type^, size_t>& _type) - { - _M_Result = _type; - } - - std::pair<_Type^, size_t> Get() - { - return std::make_pair(_M_Result.first.Get(), _M_Result.second); - } - private: - std::pair< ::Platform::Agile<_Type^>, size_t> _M_Result; - }; - -#endif /* defined (__cplusplus_winrt) */ - - // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. - // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception - // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. - struct _ExceptionHolder - { - private: - void ReportUnhandledError() - { -#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) - if (_M_winRTException != nullptr) - { - ::Platform::Details::ReportUnhandledError(_M_winRTException); - } -#endif /* defined (__cplusplus_winrt) */ - } - public: - explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : - _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) -#if defined (__cplusplus_winrt) - , _M_winRTException(nullptr) -#endif /* defined (__cplusplus_winrt) */ - { - } - -#if defined (__cplusplus_winrt) - explicit _ExceptionHolder(::Platform::Exception^ _E, const _TaskCreationCallstack &_stackTrace) : - _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) - { - } -#endif /* defined (__cplusplus_winrt) */ - - __declspec(noinline) - ~_ExceptionHolder() - { - if (_M_exceptionObserved == 0) - { - // If you are trapped here, it means an exception thrown in task chain didn't get handled. - // Please add task-based continuation to handle all exceptions coming from tasks. - // this->_M_stackTrace keeps the creation callstack of the task generates this exception. - _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); - } - } - - void _RethrowUserException() - { - if (_M_exceptionObserved == 0) - { - atomic_exchange(_M_exceptionObserved, 1l); - } - -#if defined (__cplusplus_winrt) - if (_M_winRTException != nullptr) - { - throw _M_winRTException; - } -#endif /* defined (__cplusplus_winrt) */ - std::rethrow_exception(_M_stdException); - } - - // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that - // are unobserved when the exception holder is destructed will terminate the process. - atomic_long _M_exceptionObserved; - - // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. - std::exception_ptr _M_stdException; -#if defined (__cplusplus_winrt) - ::Platform::Exception^ _M_winRTException; -#endif /* defined (__cplusplus_winrt) */ - - // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, - // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call - // is to task_completion_event::set_exception, the set_exception method was the source of the exception. - // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. - _TaskCreationCallstack _M_stackTrace; - - }; - -#if defined (__cplusplus_winrt) - /// - /// Base converter class for converting asynchronous interfaces to IAsyncOperation - /// - template - ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> - { - internal: - // The async action, action with progress or operation with progress that this stub forwards to. - ::Platform::Agile<_AsyncOperationType> _M_asyncInfo; - - Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; - - _AsyncInfoImpl( _AsyncOperationType _AsyncInfo ) : _M_asyncInfo(_AsyncInfo) {} - - public: - virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); } - virtual void Close() { _M_asyncInfo.Get()->Close(); } - - virtual property Windows::Foundation::HResult ErrorCode - { - Windows::Foundation::HResult get() - { - return _M_asyncInfo.Get()->ErrorCode; - } - } - - virtual property UINT Id - { - UINT get() - { - return _M_asyncInfo.Get()->Id; - } - } - - virtual property Windows::Foundation::AsyncStatus Status - { - Windows::Foundation::AsyncStatus get() - { - return _M_asyncInfo.Get()->Status; - } - } - - virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } - - virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ Completed - { - Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ get() - { - return _M_CompletedHandler; - } - - void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ value) - { - _M_CompletedHandler = value; - _M_asyncInfo.Get()->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) { - _M_CompletedHandler->Invoke(this, status); - }); - } - } - }; - - /// - /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation - /// - template - ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed : - _AsyncInfoImpl^, - Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, - _Result> - { - internal: - _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : - _AsyncInfoImpl^, - Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, - _Result>(_Operation) {} - - public: - virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); } - }; - - /// - /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> - /// - ref struct _IAsyncActionToAsyncOperationConverter sealed : - _AsyncInfoImpl - { - internal: - _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : - _AsyncInfoImpl(_Operation) {} - - public: - virtual details::_Unit_type GetResults() override - { - // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. - _M_asyncInfo.Get()->GetResults(); - return details::_Unit_type(); - } - }; - - /// - /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> - /// - template - ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed : - _AsyncInfoImpl^, - Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, - details::_Unit_type> - { - internal: - _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : - _AsyncInfoImpl^, - Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, - details::_Unit_type>(_Action) {} - public: - virtual details::_Unit_type GetResults() override - { - // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. - _M_asyncInfo.Get()->GetResults(); - return details::_Unit_type(); - } - }; -#endif /* defined (__cplusplus_winrt) */ -} // namespace details - -/// -/// The task_continuation_context class allows you to specify where you would like a continuation to be executed. -/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's -/// execution context is determined by the runtime, and not configurable. -/// -/// -/**/ -class task_continuation_context : public details::_ContextCallback -{ -public: - - /// - /// Creates the default task continuation context. - /// - /// - /// The default continuation context. - /// - /// - /// The default context is used if you don't specify a continuation context when you call the then method. In Windows - /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where - /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an - /// apartment aware task is the apartment where then is invoked. - /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such - /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in - /// that STA. - /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. - /// - /**/ - static task_continuation_context use_default() - { -#if defined (__cplusplus_winrt) - // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() - return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle -#else /* defined (__cplusplus_winrt) */ - return task_continuation_context(); -#endif /* defined (__cplusplus_winrt) */ - } - -#if defined (__cplusplus_winrt) - /// - /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. - /// - /// - /// A task continuation context that represents an arbitrary location. - /// - /// - /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task - /// is apartment aware. - /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment - /// aware task created in an STA. - /// This method is only available to Windows Store apps. - /// - /**/ - static task_continuation_context use_arbitrary() - { - task_continuation_context _Arbitrary(true); - _Arbitrary._Resolve(false); - return _Arbitrary; - } - - /// - /// Returns a task continuation context object that represents the current execution context. - /// - /// - /// The current execution context. - /// - /// - /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. - /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in - /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is - /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. - /// This method is only available to Windows Store apps. - /// - /**/ - static task_continuation_context use_current() - { - task_continuation_context _Current(true); - _Current._Resolve(true); - return _Current; - } -#endif /* defined (__cplusplus_winrt) */ - -private: - - task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) - { - } -}; - -class task_options; -namespace details -{ - struct _Internal_task_options - { - bool _M_hasPresetCreationCallstack; - _TaskCreationCallstack _M_presetCreationCallstack; - - void _set_creation_callstack(const _TaskCreationCallstack &_callstack) - { - _M_hasPresetCreationCallstack = true; - _M_presetCreationCallstack = _callstack; - } - _Internal_task_options() - { - _M_hasPresetCreationCallstack = false; - } - }; - - inline _Internal_task_options &_get_internal_task_options(task_options &options); - inline const _Internal_task_options &_get_internal_task_options(const task_options &options); -} -/// -/// Represents the allowed options for creating a task -/// -class task_options -{ -public: - - - /// - /// Default list of task creation options - /// - task_options() - : _M_Scheduler(get_ambient_scheduler()), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a cancellation token - /// - task_options(cancellation_token _Token) - : _M_Scheduler(get_ambient_scheduler()), - _M_CancellationToken(_Token), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(true), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a continuation context. This is valid only for continuations (then) - /// - task_options(task_continuation_context _ContinuationContext) - : _M_Scheduler(get_ambient_scheduler()), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(_ContinuationContext), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) - /// - task_options(cancellation_token _Token, task_continuation_context _ContinuationContext) - : _M_Scheduler(get_ambient_scheduler()), - _M_CancellationToken(_Token), - _M_ContinuationContext(_ContinuationContext), - _M_HasCancellationToken(false), - _M_HasScheduler(false) - { - } - - /// - /// Task option that specify a scheduler with shared lifetime - /// - template - task_options(std::shared_ptr<_SchedType> _Scheduler) - : _M_Scheduler(std::move(_Scheduler)), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option that specify a scheduler reference - /// - task_options(scheduler_interface& _Scheduler) - : _M_Scheduler(&_Scheduler), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option that specify a scheduler - /// - task_options(scheduler_ptr _Scheduler) - : _M_Scheduler(std::move(_Scheduler)), - _M_CancellationToken(cancellation_token::none()), - _M_ContinuationContext(task_continuation_context::use_default()), - _M_HasCancellationToken(false), - _M_HasScheduler(true) - { - } - - /// - /// Task option copy constructor - /// - task_options(const task_options& _TaskOptions) - : _M_Scheduler(_TaskOptions.get_scheduler()), - _M_CancellationToken(_TaskOptions.get_cancellation_token()), - _M_ContinuationContext(_TaskOptions.get_continuation_context()), - _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), - _M_HasScheduler(_TaskOptions.has_scheduler()) - { - } - - /// - /// Sets the given token in the options - /// - void set_cancellation_token(cancellation_token _Token) - { - _M_CancellationToken = _Token; - _M_HasCancellationToken = true; - } - - /// - /// Sets the given continuation context in the options - /// - void set_continuation_context(task_continuation_context _ContinuationContext) - { - _M_ContinuationContext = _ContinuationContext; - } - - /// - /// Indicates whether a cancellation token was specified by the user - /// - bool has_cancellation_token() const - { - return _M_HasCancellationToken; - } - - /// - /// Returns the cancellation token - /// - cancellation_token get_cancellation_token() const - { - return _M_CancellationToken; - } - - /// - /// Returns the continuation context - /// - task_continuation_context get_continuation_context() const - { - return _M_ContinuationContext; - } - - /// - /// Indicates whether a scheduler n was specified by the user - /// - bool has_scheduler() const - { - return _M_HasScheduler; - } - - /// - /// Returns the scheduler - /// - scheduler_ptr get_scheduler() const - { - return _M_Scheduler; - } - -private: - - task_options const& operator=(task_options const& _Right); - friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); - friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); - - scheduler_ptr _M_Scheduler; - cancellation_token _M_CancellationToken; - task_continuation_context _M_ContinuationContext; - details::_Internal_task_options _M_InternalTaskOptions; - bool _M_HasCancellationToken; - bool _M_HasScheduler; -}; - -namespace details -{ - inline _Internal_task_options & _get_internal_task_options(task_options &options) - { - return options._M_InternalTaskOptions; - } - inline const _Internal_task_options & _get_internal_task_options(const task_options &options) - { - return options._M_InternalTaskOptions; - } - - struct _Task_impl_base; - template struct _Task_impl; - - template - struct _Task_ptr - { - typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; - static _Type _Make(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } - }; - - typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; - typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; - - // The weak-typed base task handler for continuation tasks. - struct _ContinuationTaskHandleBase : _UnrealizedChore_t - { - _ContinuationTaskHandleBase * _M_next; - task_continuation_context _M_continuationContext; - bool _M_isTaskBasedContinuation; - - // This field gives inlining scheduling policy for current chore. - _TaskInliningMode_t _M_inliningMode; - - virtual _Task_ptr_base _GetTaskImplBase() const = 0; - - _ContinuationTaskHandleBase() : - _M_next(nullptr), _M_continuationContext(task_continuation_context::use_default()), _M_isTaskBasedContinuation(false), _M_inliningMode(details::_NoInline) - { - } - - virtual ~_ContinuationTaskHandleBase() {} - }; - -#if _PPLTASK_ASYNC_LOGGING && _MSC_VER >= 1800 && defined(__cplusplus_winrt) - // GUID used for identifying causality logs from PPLTask - const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); - - __declspec(selectany) volatile long _isCausalitySupported = 0; - - inline bool _IsCausalitySupported() - { -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - if (_isCausalitySupported == 0) - { - long _causality = 1; - OSVERSIONINFOEX _osvi = {}; - _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - // The Causality is supported on Windows version higher than Windows 8 - _osvi.dwMajorVersion = 6; - _osvi.dwMinorVersion = 3; - - DWORDLONG _conditionMask = 0; - VER_SET_CONDITION( _conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL ); - VER_SET_CONDITION( _conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL ); - - if ( ::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) - { - _causality = 2; - } - - _isCausalitySupported = _causality; - return _causality == 2; - } - - return _isCausalitySupported == 2 ? true : false; -#else - return true; -#endif - } - - // Stateful logger rests inside task_impl_base. - struct _TaskEventLogger - { - _Task_impl_base *_M_task; - bool _M_scheduled; - bool _M_taskPostEventStarted; - - // Log before scheduling task - void _LogScheduleTask(bool _isContinuation) - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), - _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask", 0); - _M_scheduled = true; - } - } - - // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. - void _LogCancelTask() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); - - } - } - - // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run - void _LogTaskCompleted(); - - // Log when task body (which includes user lambda and other scheduling code) begin to run - void _LogTaskExecutionStarted() { } - - // Log when task body finish executing - void _LogTaskExecutionCompleted() - { - if (_M_taskPostEventStarted && details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); - } - } - - // Log right before user lambda being invoked - void _LogWorkItemStarted() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); - } - } - - // Log right after user lambda being invoked - void _LogWorkItemCompleted() - { - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); - - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); - _M_taskPostEventStarted = true; - } - } - - _TaskEventLogger(_Task_impl_base *_task): _M_task(_task) - { - _M_scheduled = false; - _M_taskPostEventStarted = false; - } - }; - - // Exception safe logger for user lambda - struct _TaskWorkItemRAIILogger - { - _TaskEventLogger &_M_logger; - _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger): _M_logger(_taskHandleLogger) - { - _M_logger._LogWorkItemStarted(); - } - - ~_TaskWorkItemRAIILogger() - { - _M_logger._LogWorkItemCompleted(); - } - _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned - }; - -#else - inline void _LogCancelTask(_Task_impl_base *) {} - struct _TaskEventLogger - { - void _LogScheduleTask(bool) {} - void _LogCancelTask() {} - void _LogWorkItemStarted() {} - void _LogWorkItemCompleted() {} - void _LogTaskExecutionStarted() {} - void _LogTaskExecutionCompleted() {} - void _LogTaskCompleted() {} - _TaskEventLogger(_Task_impl_base *) {} - }; - struct _TaskWorkItemRAIILogger - { - _TaskWorkItemRAIILogger(_TaskEventLogger &) {} - }; -#endif - - /// - /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler - /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. - /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from - /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. - /// - /// - /// The result type of the _Task_impl. - /// - /// - /// The derived task handle class. The operator () needs to be implemented. - /// - /// - /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. - /// - template - struct _PPLTaskHandle : _BaseTaskHandle - { - _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) - { - } - - virtual ~_PPLTaskHandle() - { - // Here is the sink of all task completion code paths - _M_pTask->_M_taskEventLogger._LogTaskCompleted(); - } - - virtual void invoke() const - { - // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled - // by the runtime. - _ASSERTE((bool)_M_pTask); - if (!_M_pTask->_TransitionedToStarted()) - { - static_cast(this)->_SyncCancelAndPropagateException(); - return; - } - - _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); - try - { - // All derived task handle must implement this contract function. - static_cast(this)->_Perform(); - } - catch(const task_canceled &) - { - _M_pTask->_Cancel(true); - } - catch(const _Interruption_exception &) - { - _M_pTask->_Cancel(true); - } -#if defined (__cplusplus_winrt) - catch(::Platform::Exception^ _E) - { - _M_pTask->_CancelWithException(_E); - } -#endif /* defined (__cplusplus_winrt) */ - catch(...) - { - _M_pTask->_CancelWithException(std::current_exception()); - } - _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); - } - - // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. - // The return value should be automatically optimized by R-value ref. - _Task_ptr_base _GetTaskImplBase() const - { - return _M_pTask; - } - - typename _Task_ptr<_ReturnType>::_Type _M_pTask; - private: - _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator - }; - - /// - /// The base implementation of a first-class task. This class contains all the non-type specific - /// implementation details of the task. - /// - /**/ - struct _Task_impl_base - { - enum _TaskInternalState - { - // Tracks the state of the task, rather than the task collection on which the task is scheduled - _Created, - _Started, - _PendingCancel, - _Completed, - _Canceled - }; -// _M_taskEventLogger - 'this' : used in base member initializer list -#if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4355) -#endif - _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) - : _M_TaskState(_Created), - _M_fFromAsync(false), _M_fUnwrappedTask(false), - _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), - _M_taskEventLogger(this) - { - // Set cancellation token - _M_pTokenState = _PTokenState; - _ASSERTE(_M_pTokenState != nullptr); - if (_M_pTokenState != _CancellationTokenState::_None()) - _M_pTokenState->_Reference(); - } -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - - virtual ~_Task_impl_base() - { - _ASSERTE(_M_pTokenState != nullptr); - if (_M_pTokenState != _CancellationTokenState::_None()) - { - _M_pTokenState->_Release(); - } - } - - task_status _Wait() - { - bool _DoWait = true; - -#if defined (__cplusplus_winrt) - if (_IsNonBlockingThread()) - { - // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal - // if task has not been completed. - if (!_IsCompleted() && !_IsCanceled()) - { - throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); - } - else - { - // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation - // needs to be marshaled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM - // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which - // task based continuations are wont to do), waiting on the task group results in on the chore that is making this - // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on - // if it has finished execution (which means now we are on the inline synchronous callback). - _DoWait = false; - } - } -#endif /* defined (__cplusplus_winrt) */ - if (_DoWait) - { - // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The - // async operation will take place on a thread in the appropriate apartment Simply wait for the completed - // event to be set. - if (_M_fFromAsync) - { - _M_TaskCollection._Wait(); - } - else - { - // Wait on the task collection to complete. The task collection is guaranteed to still be - // valid since the task must be still within scope so that the _Task_impl_base destructor - // has not yet been called. This call to _Wait potentially inlines execution of work. - try - { - // Invoking wait on a task collection resets the state of the task collection. This means that - // if the task collection itself were canceled, or had encountered an exception, only the first - // call to wait will receive this status. However, both cancellation and exceptions flowing through - // tasks set state in the task impl itself. - - // When it returns canceled, either work chore or the cancel thread should already have set task's state - // properly -- canceled state or completed state (because there was no interruption point). - // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. - _M_TaskCollection._RunAndWait(); - } - catch(details::_Interruption_exception&) - { - // The _TaskCollection will never be an interruption point since it has a none token. - _ASSERTE(false); - } - catch(task_canceled&) - { - // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task - // must be called from code that is executed within the task (throwing it from parallel work created by and waited - // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen - // the exception and canceled the task. Swallow the exception here. - _ASSERTE(_IsCanceled()); - } -#if defined (__cplusplus_winrt) - catch(::Platform::Exception^ _E) - { - // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. - if(!_HasUserException()) - { - _CancelWithException(_E); - } - // Rethrow will mark the exception as observed. - _M_exceptionHolder->_RethrowUserException(); - } -#endif /* defined (__cplusplus_winrt) */ - catch(...) - { - // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. - if(!_HasUserException()) - { - _CancelWithException(std::current_exception()); - } - // Rethrow will mark the exception as observed. - _M_exceptionHolder->_RethrowUserException(); - } - - // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task - // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must - // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; - // however, this takes the tact of simply waiting upon the completion signal. - if (_M_fUnwrappedTask) - { - _M_TaskCollection._Wait(); - } - } - } - - if (_HasUserException()) - { - _M_exceptionHolder->_RethrowUserException(); - } - else if (_IsCanceled()) - { - return canceled; - } - _ASSERTE(_IsCompleted()); - return completed; - } - - /// - /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. - /// - /// - /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task - /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at - /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could - /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. - /// - /// - /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. - /// - /// - /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when - /// _UserException is set to true. - /// - /// - /// The exception holder that represents the exception. Only valid when _UserException is set to true. - /// - virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; - - bool _Cancel(bool _SynchronousCancel) - { - // Send in a dummy value for exception. It is not used when the first parameter is false. - return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); - } - - bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) - { - // This task was canceled because an ancestor task encountered an exception. - return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); - } - -#if defined (__cplusplus_winrt) - bool _CancelWithException(::Platform::Exception^ _Exception) - { - // This task was canceled because the task body encountered an exception. - _ASSERTE(!_HasUserException()); - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); - } -#endif /* defined (__cplusplus_winrt) */ - - bool _CancelWithException(const std::exception_ptr& _Exception) - { - // This task was canceled because the task body encountered an exception. - _ASSERTE(!_HasUserException()); - return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); - } - - void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) - { - _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); - - auto _CancellationCallback = [_WeakPtr](){ - // Taking ownership of the task prevents dead lock during destruction - // if the destructor waits for the cancellations to be finished - auto _task = _WeakPtr.lock(); - if (_task != nullptr) - _task->_Cancel(false); - }; - - _M_pRegistration = new details::_CancellationTokenCallback(_CancellationCallback); - _M_pTokenState->_RegisterCallback(_M_pRegistration); - } - - void _DeregisterCancellation() - { - if (_M_pRegistration != nullptr) - { - _M_pTokenState->_DeregisterCallback(_M_pRegistration); - _M_pRegistration->_Release(); - _M_pRegistration = nullptr; - } - } - - bool _IsCreated() - { - return (_M_TaskState == _Created); - } - - bool _IsStarted() - { - return (_M_TaskState == _Started); - } - - bool _IsPendingCancel() - { - return (_M_TaskState == _PendingCancel); - } - - bool _IsCompleted() - { - return (_M_TaskState == _Completed); - } - - bool _IsCanceled() - { - return (_M_TaskState == _Canceled); - } - - bool _HasUserException() - { - return static_cast(_M_exceptionHolder); - } - - const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() - { - _ASSERTE(_HasUserException()); - return _M_exceptionHolder; - } - - bool _IsApartmentAware() - { - return _M_fFromAsync; - } - - void _SetAsync(bool _Async = true) - { - _M_fFromAsync = _Async; - } - - _TaskCreationCallstack _GetTaskCreationCallstack() - { - return _M_pTaskCreationCallstack; - } - - void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) - { - _M_pTaskCreationCallstack = _Callstack; - } - - /// - /// Helper function to schedule the task on the Task Collection. - /// - /// - /// The task chore handle that need to be executed. - /// - /// - /// The inlining scheduling policy for current _PTaskHandle. - /// - void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) - { - try - { - _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); - } - catch(const task_canceled &) - { - // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task - // must be called from code that is executed within the task (throwing it from parallel work created by and waited - // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen - // the exception and canceled the task. Swallow the exception here. - _ASSERTE(_IsCanceled()); - } - catch(const _Interruption_exception &) - { - // The _TaskCollection will never be an interruption point since it has a none token. - _ASSERTE(false); - } - catch(...) - { - // The exception could have come from two places: - // 1. From the chore body, so it already should have been caught and canceled. - // In this case swallow the exception. - // 2. From trying to actually schedule the task on the scheduler. - // In this case cancel the task with the current exception, otherwise the - // task will never be signaled leading to deadlock when waiting on the task. - if (!_HasUserException()) - { - _CancelWithException(std::current_exception()); - } - } - } - - /// - /// Function executes a continuation. This function is recorded by a parent task implementation - /// when a continuation is created in order to execute later. - /// - /// - /// The continuation task chore handle that need to be executed. - /// - /**/ - void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) - { - _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); - if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) - { - if (_HasUserException()) - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); - } - else - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - _ImplBase->_Cancel(true); - } - } - else - { - // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled - // (with or without a user exception). - _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); - _ASSERTE(!_ImplBase->_IsCanceled()); - return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); - } - - // If the handle is not scheduled, we need to manually delete it. - delete _PTaskHandle; - } - - // Schedule a continuation to run - void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) - { - - _M_taskEventLogger._LogScheduleTask(true); - // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) - if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) - { - // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, - // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce - // the cost of marshaling. - // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. - if (_PTaskHandle->_M_inliningMode != details::_ForceInline) - { - _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; - } - _ScheduleFuncWithAutoInline([_PTaskHandle]() { - // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. - // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. - auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); - if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) - { - _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); - } - else - { - // - // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle - // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: - // - // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into - // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will - // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). - // - try - { - // Dev10 compiler needs this! - auto _PTaskHandle1 = _PTaskHandle; - _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ - _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); - }); - } -#if defined (__cplusplus_winrt) - catch(::Platform::Exception^ _E) - { - _TaskImplPtr->_CancelWithException(_E); - } -#endif /* defined (__cplusplus_winrt) */ - catch(...) - { - _TaskImplPtr->_CancelWithException(std::current_exception()); - } - } - }, _PTaskHandle->_M_inliningMode); - } - else - { - _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); - } - } - - /// - /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation - /// if the task has completed or append it to a list of functions to execute when the task actually does complete. - /// - /// - /// The input type of the task. - /// - /// - /// The output type of the task. - /// - /**/ - void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) - { - enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; - - // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. - // Otherwise, add it to the list of pending continuations - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); - if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) - { - _Do = _Schedule; - } - else if (_IsCanceled()) - { - if (_HasUserException()) - { - _Do = _CancelWithException; - } - else - { - _Do = _Cancel; - } - } - else - { - // chain itself on the continuation chain. - _PTaskHandle->_M_next = _M_Continuations; - _M_Continuations = _PTaskHandle; - } - } - - // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of - // async tasks may execute inline. - switch (_Do) - { - case _Schedule: - { - _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); - break; - } - case _Cancel: - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - _PTaskHandle->_GetTaskImplBase()->_Cancel(true); - - delete _PTaskHandle; - break; - } - case _CancelWithException: - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); - - delete _PTaskHandle; - break; - } - case _Nothing: - default: - // In this case, we have inserted continuation to continuation chain, - // nothing more need to be done, just leave. - break; - } - } - - void _RunTaskContinuations() - { - // The link list can no longer be modified at this point, - // since all following up continuations will be scheduled by themselves. - _ContinuationList _Cur = _M_Continuations, _Next; - _M_Continuations = nullptr; - while (_Cur) - { - // Current node might be deleted after running, - // so we must fetch the next first. - _Next = _Cur->_M_next; - _RunContinuation(_Cur); - _Cur = _Next; - } - } - -#if defined (__cplusplus_winrt) - static bool _IsNonBlockingThread() - { - APTTYPE _AptType; - APTTYPEQUALIFIER _AptTypeQualifier; - - HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); - // - // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. - // - if (SUCCEEDED(hr)) - { - switch(_AptType) - { - case APTTYPE_STA: - case APTTYPE_MAINSTA: - return true; - break; - case APTTYPE_NA: - switch(_AptTypeQualifier) - { - // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed - // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting - // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the - // thread out of circulation for a while. - case APTTYPEQUALIFIER_NA_ON_STA: - case APTTYPEQUALIFIER_NA_ON_MAINSTA: - return true; - break; - } - break; - } - } - -#if _UITHREADCTXT_SUPPORT - // This method is used to throw an exception in _Wait() if called within STA. We - // want the same behavior if _Wait is called on the UI thread. - if (SUCCEEDED(CaptureUiThreadContext(nullptr))) - { - return true; - } -#endif /* _UITHREADCTXT_SUPPORT */ - - return false; - } - - template - static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, - Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) - { - // This method is invoked either when a task is created from an existing async operation or - // when a lambda that creates an async operation executes. - - // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on - // the IAsyncInfo object will be released when all ^references to the operation go out of scope. - - // This assertion uses the existence of taskcollection to determine if the task was created from an event. - // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection - // when a custom scheduler is used. - // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); - - // Pass the shared_ptr by value into the lambda instead of using 'this'. - _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( - [_OuterTask](Windows::Foundation::IAsyncOperation::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable - { - if (_Status == Windows::Foundation::AsyncStatus::Canceled) - { - _OuterTask->_Cancel(true); - } - else if (_Status == Windows::Foundation::AsyncStatus::Error) - { - _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast(_Operation->ErrorCode.Value))); - } - else - { - _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); - _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); - } - - // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could - // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold - // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from - // it using the Windows Runtime Async APIs causes a sharing violation. - // Using const_cast is the workaround for failed mutable keywords - const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); - }); - _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); - } -#endif /* defined (__cplusplus_winrt) */ - - template - static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) - { - _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); - - // - // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the - // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation - // off the inner task which does the appropriate funneling to the outer one. We use _Then instead of then to prevent - // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless - // of whether or not the _OuterTask task is canceled. - // - _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { - - if (_AncestorTask._GetImpl()->_IsCompleted()) - { - _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); - } - else - { - _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); - if (_AncestorTask._GetImpl()->_HasUserException()) - { - // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. - // Instead, it is the enclosing task. - _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); - } - else - { - _OuterTask->_Cancel(true); - } - } - }, nullptr, details::_DefaultAutoInline); - - } - - scheduler_ptr _GetScheduler() const - { - return _M_TaskCollection._GetScheduler(); - } - - // Tracks the internal state of the task - std::atomic<_TaskInternalState> _M_TaskState; - // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an - // async operation or async action that is unwrapped by the runtime. - bool _M_fFromAsync; - // Set to true when a continuation unwraps a task or async operation. - bool _M_fUnwrappedTask; - - // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. - // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception - // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. - std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; - - ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec; - - // The cancellation token state. - _CancellationTokenState * _M_pTokenState; - - // The registration on the token. - _CancellationTokenRegistration * _M_pRegistration; - - typedef _ContinuationTaskHandleBase * _ContinuationList; - _ContinuationList _M_Continuations; - - // The async task collection wrapper - ::pplx::details::_TaskCollection_t _M_TaskCollection; - - // Callstack for function call (constructor or .then) that created this task impl. - _TaskCreationCallstack _M_pTaskCreationCallstack; - - _TaskEventLogger _M_taskEventLogger; - private: - // Must not be copied by value: - _Task_impl_base(const _Task_impl_base&); - _Task_impl_base const & operator=(_Task_impl_base const&); - }; - -#if _PPLTASK_ASYNC_LOGGING && _MSC_VER >= 1800 && defined(__cplusplus_winrt) - inline void _TaskEventLogger::_LogTaskCompleted() - { - if (_M_scheduled) - { - ::Windows::Foundation::AsyncStatus _State; - if (_M_task->_IsCompleted()) - _State = ::Windows::Foundation::AsyncStatus::Completed; - else if (_M_task->_HasUserException()) - _State = ::Windows::Foundation::AsyncStatus::Error; - else - _State = ::Windows::Foundation::AsyncStatus::Canceled; - - if (details::_IsCausalitySupported()) - { - ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, - _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); - } - } - } -#endif - - /// - /// The implementation of a first-class task. This structure contains the task group used to execute - /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr - /// member of the the public task class, so its destruction is handled automatically. - /// - /// - /// The result type of this task. - /// - /**/ - template - struct _Task_impl : public _Task_impl_base - { -#if defined (__cplusplus_winrt) - typedef Windows::Foundation::IAsyncOperation::_Value> _AsyncOperationType; -#endif // defined(__cplusplus_winrt) - _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) - : _Task_impl_base(_Ct, _Scheduler_arg) - { -#if defined (__cplusplus_winrt) - _M_unwrapped_async_op = nullptr; -#endif /* defined (__cplusplus_winrt) */ - } - - virtual ~_Task_impl() - { - // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause - // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. - _DeregisterCancellation(); - } - - virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) - { - bool _RunContinuations = false; - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); - if (_UserException) - { - _ASSERTE(_SynchronousCancel && !_IsCompleted()); - // If the state is _Canceled, the exception has to be coming from an ancestor. - _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); - - // We should not be canceled with an exception more than once. - _ASSERTE(!_HasUserException()); - - // Mark _PropagatedFromAncestor as used. - (void)_PropagatedFromAncestor; - - if (_M_TaskState == _Canceled) - { - // If the task has finished canceling there should not be any continuation records in the array. - return false; - } - else - { - _ASSERTE(_M_TaskState != _Completed); - _M_exceptionHolder = _ExceptionHolder_arg; - } - } - else - { - // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel - // which is to say, cancellation is already initiated, so return early. - if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) - { - _ASSERTE(!_IsCompleted() || !_HasUserException()); - return false; - } - _ASSERTE(!_SynchronousCancel || !_HasUserException()); - } - - if (_SynchronousCancel) - { - // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() - _M_TaskState = _Canceled; - // Cancellation completes the task, so all dependent tasks must be run to cancel them - // They are canceled when they begin running (see _RunContinuation) and see that their - // ancestor has been canceled. - _RunContinuations = true; - } - else - { - _ASSERTE(!_UserException); - - if (_IsStarted()) - { -#if defined (__cplusplus_winrt) - if (_M_unwrapped_async_op != nullptr) - { - // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. - _M_unwrapped_async_op->Cancel(); - } -#endif /* defined (__cplusplus_winrt) */ - _M_TaskCollection._Cancel(); - } - - // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). - // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from - // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. - _M_TaskState = _PendingCancel; - - _M_taskEventLogger._LogCancelTask(); - } - - - } - - // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. - if (_RunContinuations) - { - _M_TaskCollection._Complete(); - - if (_M_Continuations) - { - // Scheduling cancellation with automatic inlining. - _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); - } - } - return true; - } - - void _FinalizeAndRunContinuations(_ReturnType _Result) - { - _M_Result.Set(_Result); - - { - // - // Hold this lock to ensure continuations being concurrently either get added - // to the _M_Continuations vector or wait for the result - // - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); - - // A task could still be in the _Created state if it was created with a task_completion_event. - // It could also be in the _Canceled state for the same reason. - _ASSERTE(!_HasUserException() && !_IsCompleted()); - if (_IsCanceled()) - { - return; - } - - // Always transition to "completed" state, even in the face of unacknowledged pending cancellation - _M_TaskState = _Completed; - } - _M_TaskCollection._Complete(); - _RunTaskContinuations(); - } - - // - // This method is invoked when the starts executing. The task returns early if this method returns true. - // - bool _TransitionedToStarted() - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); - // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. - _ASSERTE(!_IsCanceled()); - if (_IsPendingCancel()) - return false; - - _ASSERTE(_IsCreated()); - _M_TaskState = _Started; - return true; - } - -#if defined (__cplusplus_winrt) - void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); - // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. - if (_IsPendingCancel()) - { - _ASSERTE(!_IsCanceled()); - _AsyncOp->Cancel(); - } - else - { - _M_unwrapped_async_op = _AsyncOp; - } - } -#endif /* defined (__cplusplus_winrt) */ - - // Return true if the task has reached a terminal state - bool _IsDone() - { - return _IsCompleted() || _IsCanceled(); - } - - _ReturnType _GetResult() - { - return _M_Result.Get(); - } - - _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. -#if defined (__cplusplus_winrt) - _AsyncOperationType^ _M_unwrapped_async_op; -#endif /* defined (__cplusplus_winrt) */ - }; - - template - struct _Task_completion_event_impl - { - private: - _Task_completion_event_impl(const _Task_completion_event_impl&); - _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); - - public: - - typedef std::vector::_Type> _TaskList; - - _Task_completion_event_impl() : - _M_fHasValue(false), _M_fIsCanceled(false) - { - } - - bool _HasUserException() - { - return _M_exceptionHolder != nullptr; - } - - ~_Task_completion_event_impl() - { - for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) - { - _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); - // Cancel the tasks since the event was never signaled or canceled. - (*_TaskIt)->_Cancel(true); - } - } - - // We need to protect the loop over the array, so concurrent_vector would not have helped - _TaskList _M_tasks; - ::pplx::extensibility::critical_section_t _M_taskListCritSec; - _ResultHolder<_ResultType> _M_value; - std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; - std::atomic _M_fHasValue; - std::atomic _M_fIsCanceled; - }; - - // Utility method for dealing with void functions - inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function& _Func) - { - return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; - } - - template - std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) - { - return [=](_Unit_type) -> _Type { return _Func(); }; - } - - template - std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function& _Func) - { - return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; - } - - inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function& _Func) - { - return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; - } -} // namespace details - -/// -/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, -/// or start a task in response to an external event. -/// -/// -/// The result type of this task_completion_event class. -/// -/// -/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and -/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must -/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type -/// will cause the associated task to complete, and provide that value as a result to its continuations. -/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. -/// task_completion_event behaves like a smart pointer, and should be passed by value. -/// -/// -/**/ -template -class task_completion_event -{ -public: - /// - /// Constructs a task_completion_event object. - /// - /**/ - task_completion_event() - : _M_Impl(std::make_shared>()) - { - } - - /// - /// Sets the task completion event. - /// - /// - /// The result to set this event with. - /// - /// - /// The method returns true if it was successful in setting the event. It returns false if the event is already set. - /// - /// - /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the - /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the - /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have - /// a other than void will pass the value to their continuations. - /// - /**/ - bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. - if (_IsTriggered()) - { - return false; - } - - _TaskList _Tasks; - bool _RunContinuations = false; - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); - - if (!_IsTriggered()) - { - _M_Impl->_M_value.Set(_Result); - _M_Impl->_M_fHasValue = true; - - _Tasks.swap(_M_Impl->_M_tasks); - _RunContinuations = true; - } - } - - if (_RunContinuations) - { - for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) - { - // If current task was canceled by a cancellation_token, it would be in cancel pending state. - if ((*_TaskIt)->_IsPendingCancel()) - (*_TaskIt)->_Cancel(true); - else - { - // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all - // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we - // need to run continuations after the lock is released. - (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); - } - } - if (_M_Impl->_HasUserException()) - { - _M_Impl->_M_exceptionHolder.reset(); - } - return true; - } - - return false; - } - - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. - return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); - } - - /// - /// Propagates an exception to all tasks associated with this event. - /// - /// - /// The exception_ptr that indicates the exception to set this event with. - /// - /**/ - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. - return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); - } - - /// - /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has - /// not already been set. - /// - bool _Cancel() const - { - // Cancel with the stored exception if one exists. - return _CancelInternal(); - } - - /// - /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled - /// with the same exception. - /// - template - bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const - { - bool _Canceled; - if(_StoreException(_ExHolder, _SetExceptionAddressHint)) - { - _Canceled = _CancelInternal(); - _ASSERTE(_Canceled); - } - else - { - _Canceled = false; - } - return _Canceled; - } - - /// - /// Internal method that stores an exception in the task completion event. This is used internally by when_any. - /// Note, this does not cancel the task completion event. A task completion event with a stored exception - /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. - /// - template - bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); - if (!_IsTriggered() && !_M_Impl->_HasUserException()) - { - // Create the exception holder only if we have ensured there we will be successful in setting it onto the - // task completion event. Failing to do so will result in an unobserved task exception. - _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); - return true; - } - return false; - } - - /// - /// Tests whether current event has been either Set, or Canceled. - /// - bool _IsTriggered() const - { - return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; - } - -private: - - static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) - { - return _ExHolder; - } - - static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) - { - return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); - } - - - template friend class task; // task can register itself with the event by calling the private _RegisterTask - template friend class task_completion_event; - - typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; - - /// - /// Cancels the task_completion_event. - /// - bool _CancelInternal() const - { - // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal - // will never be invoked if the task completion event has been set. - _ASSERTE(!_M_Impl->_M_fHasValue); - if (_M_Impl->_M_fIsCanceled) - { - return false; - } - - _TaskList _Tasks; - bool _Cancel = false; - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); - _ASSERTE(!_M_Impl->_M_fHasValue); - if (!_M_Impl->_M_fIsCanceled) - { - _M_Impl->_M_fIsCanceled = true; - _Tasks.swap(_M_Impl->_M_tasks); - _Cancel = true; - } - } - - bool _UserException = _M_Impl->_HasUserException(); - - if (_Cancel) - { - for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) - { - // Need to call this after the lock is released. See comments in set(). - if (_UserException) - { - (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); - } - else - { - (*_TaskIt)->_Cancel(true); - } - } - } - return _Cancel; - } - - /// - /// Register a task with this event. This function is called when a task is constructed using - /// a task_completion_event. - /// - void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) - { - ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); - - //If an exception was already set on this event, then cancel the task with the stored exception. - if(_M_Impl->_HasUserException()) - { - _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); - } - else if (_M_Impl->_M_fHasValue) - { - _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); - } - else - { - _M_Impl->_M_tasks.push_back(_TaskParam); - } - } - - std::shared_ptr> _M_Impl; -}; - -/// -/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, -/// or start a task in response to an external event. -/// -/// -/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and -/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must -/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type -/// will cause the associated task to complete, and provide that value as a result to its continuations. -/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. -/// task_completion_event behaves like a smart pointer, and should be passed by value. -/// -/// -/**/ -template<> -class task_completion_event -{ -public: - /// - /// Sets the task completion event. - /// - /// - /// The method returns true if it was successful in setting the event. It returns false if the event is already set. - /// - /// - /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the - /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the - /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have - /// a other than void will pass the value to their continuations. - /// - /**/ - bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - return _M_unitEvent.set(details::_Unit_type()); - } - - template - __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result - bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); - } - - /// - /// Propagates an exception to all tasks associated with this event. - /// - /// - /// The exception_ptr that indicates the exception to set this event with. - /// - /**/ - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK intrinsic gives us the expected result - bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. - return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); - } - - /// - /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has - /// not already been set. - /// - void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas - { - _M_unitEvent._Cancel(); - } - - /// - /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled - /// with the same exception. - /// - void _Cancel(const std::shared_ptr& _ExHolder) const - { - _M_unitEvent._Cancel(_ExHolder); - } - - /// - /// Method that stores an exception in the task completion event. This is used internally by when_any. - /// Note, this does not cancel the task completion event. A task completion event with a stored exception - /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. - /// - bool _StoreException(const std::shared_ptr& _ExHolder) const - { - return _M_unitEvent._StoreException(_ExHolder); - } - - /// - /// Test whether current event has been either Set, or Canceled. - /// - bool _IsTriggered() const - { - return _M_unitEvent._IsTriggered(); - } - -private: - template friend class task; // task can register itself with the event by calling the private _RegisterTask - - /// - /// Register a task with this event. This function is called when a task is constructed using - /// a task_completion_event. - /// - void _RegisterTask(details::_Task_ptr::_Type _TaskParam) - { - _M_unitEvent._RegisterTask(_TaskParam); - } - - // The void event contains an event a dummy type so common code can be used for events with void and non-void results. - task_completion_event _M_unitEvent; -}; - -namespace details -{ - // - // Compile-time validation helpers - // - - // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. - // - // Anything callable is fine - template - auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); - -#if defined (__cplusplus_winrt) - // Anything that has GetResults is fine: this covers all async operations - template - auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); -#endif - - // Allow parameters with set: this covers task_completion_event - template - auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); - - template - auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); - - // All else is invalid - template - std::false_type _IsValidTaskCtor(_Ty _Param, ...); - - template - void _ValidateTaskConstructorArgs(_Ty _Param) - { - static_assert(std::is_same(_Param,0,0,0,0)),std::true_type>::value, -#if defined (__cplusplus_winrt) - "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" -#else /* defined (__cplusplus_winrt) */ - "incorrect argument for task constructor; can be a callable object or a task_completion_event" -#endif /* defined (__cplusplus_winrt) */ - ); -#if defined (__cplusplus_winrt) - static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), - "incorrect template argument for task; consider using the return type of the async operation"); -#endif /* defined (__cplusplus_winrt) */ - } - -#if defined (__cplusplus_winrt) - // Helpers for create_async validation - // - // A parameter lambda taking no arguments is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); - - // A parameter lambda taking an cancellation_token argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); - - // A parameter lambda taking a progress report argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); - - // A parameter lambda taking a progress report and a cancellation_token argument is valid - template - static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); - - // All else is invalid - template - static std::false_type _IsValidCreateAsync(_Ty _Param, ...); -#endif /* defined (__cplusplus_winrt) */ - -/// -/// A helper class template that makes only movable functions be able to be passed to std::function -/// - template - struct _NonCopyableFunctorWrapper - { - template, - typename std::decay<_Tx>::type>::value>::type> - explicit _NonCopyableFunctorWrapper(_Tx&& f) - : _M_functor{std::make_shared<_Ty>(std::forward<_Tx>(f))} - {} - - template - auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) - { - return _M_functor->operator()(std::forward<_Args>(args)...); - } - - template - auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) - { - return _M_functor->operator()(std::forward<_Args>(args)...); - } - - std::shared_ptr<_Ty> _M_functor; - }; - - template - struct _CopyableFunctor - { - typedef _Ty _Type; - }; - - template - struct _CopyableFunctor<_Ty, typename std::enable_if< - std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type> - { - typedef _NonCopyableFunctorWrapper<_Ty> _Type; - }; -} -/// -/// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a -/// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. -/// -template -class _Continuation_func_transformer -{ -public: - static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) - { - return _Func; - } -}; - -template -class _Continuation_func_transformer -{ -public: - static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) - { - return details::_MakeUnitToTFunc<_OutType>(_Func); - } -}; - -template -class _Continuation_func_transformer<_InType, void> -{ -public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) - { - return details::_MakeTToUnitFunc<_InType>(_Func); - } -}; - -template<> -class _Continuation_func_transformer -{ -public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) - { - return details::_MakeUnitToUnitFunc(_Func); - } -}; - -// A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used -// to substitute for void). This is to minimize the special handling required for 'void'. -template -class _Init_func_transformer -{ -public: - static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) - { - return _Func; - } -}; - -template<> -class _Init_func_transformer -{ -public: - static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) - { - return details::_MakeVoidToUnitFunc(_Func); - } -}; - -/// -/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, -/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces -/// a result of type on successful completion. Tasks of type task<void> produce no result. -/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using -/// continuations(then), and join(when_all) and choice(when_any) patterns. -/// -/// -/// The result type of this task. -/// -/// -/// For more information, see . -/// -/**/ -template -class task -{ -public: - /// - /// The type of the result an object of this class produces. - /// - /**/ - typedef _ReturnType result_type; - - /// - /// Constructs a task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task() : _M_Impl(nullptr) - { - // The default constructor should create a task with a nullptr impl. This is a signal that the - // task is not usable and should throw if any wait(), get() or then() APIs are used. - } - - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives - /// the token cancellation_token::none(). - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - explicit task(_Ty _Param) - { - task_options _TaskOptions; - details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); - - _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); - // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. - _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); - - _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The task options include cancellation token, scheduler etc - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - explicit task(_Ty _Param, const task_options &_TaskOptions) - { - details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); - - _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); - // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. - _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); - - _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(const task& _Other): _M_Impl(_Other._M_Impl) {} - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(const task& _Other) - { - if (this != &_Other) - { - _M_Impl = _Other._M_Impl; - } - return *this; - } - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(task&& _Other) - { - if (this != &_Other) - { - _M_Impl = std::move(_Other._M_Impl); - } - return *this; - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(_Function&& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - task_options _TaskOptions; - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The task options include cancellation token, scheduler and continuation context. By default the former 3 - /// options are inherited from the antecedent task - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(_Function&& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store - /// style app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - task_options _TaskOptions(_CancellationToken, _ContinuationContext); - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); - } - - /// - /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks - /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. - /// - /// - /// A task_status value which could be either completed or canceled. If the task encountered an exception - /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. - /// - /**/ - task_status wait() const - { - if (!_M_Impl) - { - throw invalid_operation("wait() cannot be called on a default constructed task."); - } - - return _M_Impl->_Wait(); - } - - /// - /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to - /// finish. This method does not return a value when called on a task with a result_type of void. - /// - /// - /// The result of the task. - /// - /// - /// If the task is canceled, a call to get will throw a task_canceled exception. If the task - /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. - /// - /**/ - _ReturnType get() const - { - if (!_M_Impl) - { - throw invalid_operation("get() cannot be called on a default constructed task."); - } - - if (_M_Impl->_Wait() == canceled) - { - throw task_canceled(); - } - - return _M_Impl->_GetResult(); - } - - /// - /// Determines if the task is completed. - /// - /// - /// True if the task has completed, false otherwise. - /// - /// - /// The function returns true if the task is completed or canceled (with or without user exception). - /// - bool is_done() const - { - if (!_M_Impl) - { - throw invalid_operation("is_done() cannot be called on a default constructed task."); - } - - return _M_Impl->_IsDone(); - } - - /// - /// Returns the scheduler for this task - /// - /// - /// A pointer to the scheduler - /// - scheduler_ptr scheduler() const - { - if (!_M_Impl) - { - throw invalid_operation("scheduler() cannot be called on a default constructed task."); - } - - return _M_Impl->_GetScheduler(); - } - - /// - /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. - /// - /// - /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. - /// - /**/ - bool is_apartment_aware() const - { - if (!_M_Impl) - { - throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); - } - return _M_Impl->_IsApartmentAware(); - } - - /// - /// Determines whether two task objects represent the same internal task. - /// - /// - /// true if the objects refer to the same underlying task, and false otherwise. - /// - /**/ - bool operator==(const task<_ReturnType>& _Rhs) const - { - return (_M_Impl == _Rhs._M_Impl); - } - - /// - /// Determines whether two task objects represent different internal tasks. - /// - /// - /// true if the objects refer to different underlying tasks, and false otherwise. - /// - /**/ - bool operator!=(const task<_ReturnType>& _Rhs) const - { - return !operator==(_Rhs); - } - - /// - /// Create an underlying task implementation. - /// - void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) - { - _ASSERTE(_Ct != nullptr); - _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); - if (_Ct != details::_CancellationTokenState::_None()) - { - _M_Impl->_RegisterCancellation(_M_Impl); - } - } - - /// - /// Return the underlying implementation for this task. - /// - const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const - { - return _M_Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementation. - /// - void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) - { - _ASSERTE(!_M_Impl); - _M_Impl = _Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementation using a move instead of a copy. - /// - void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) - { - _ASSERTE(!_M_Impl); - _M_Impl = std::move(_Impl); - } - - /// - /// Sets a property determining whether the task is apartment aware. - /// - void _SetAsync(bool _Async = true) - { - _GetImpl()->_SetAsync(_Async); - } - - /// - /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. - /// - void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) - { - _GetImpl()->_SetTaskCreationCallstack(_callstack); - } - - /// - /// An internal version of then that takes additional flags and always execute the continuation inline by default. - /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. - /// This function is Used for runtime internal continuations only. - /// - template - auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, - details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType - { - // inherit from antecedent - auto _Scheduler = _GetImpl()->_GetScheduler(); - - return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); - } - -private: - template friend class task; - - - // The task handle type used to construct an 'initial task' - a task with no dependents. - template - struct _InitialTaskHandle : - details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> - { - _Function _M_function; - _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) - : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) - , _M_function(_func) - { - } - - virtual ~_InitialTaskHandle() {} - - template - auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) - { - details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); - return _func(); - } - - void _Perform() const - { - _Init(_TypeSelection()); - } - - void _SyncCancelAndPropagateException() const - { - this->_M_pTask->_Cancel(true); - } - - // - // Overload 0: returns _InternalReturnType - // - // This is the most basic task with no unwrapping - // - void _Init(details::_TypeSelectorNoAsync) const - { - this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); - } - - // - // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only under /ZW) - // or - // returns task<_InternalReturnType> - // - // This is task whose functor returns an async operation or a task which will be unwrapped for continuation - // Depending on the output type, the right _AsyncInit gets invoked - // - void _Init(details::_TypeSelectorAsyncOperationOrTask) const - { - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); - } - -#if defined (__cplusplus_winrt) - // - // Overload 2: returns IAsyncAction^ - // - // This is task whose functor returns an async action which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncAction) const - { - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); - } - - // - // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ - // - // This is task whose functor returns an async operation with progress which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncOperationWithProgress) const - { - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, - ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); - } - - // - // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ - // - // This is task whose functor returns an async action with progress which will be unwrapped for continuation - // - void _Init(details::_TypeSelectorAsyncActionWithProgress) const - { - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, - ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); - } -#endif /* defined (__cplusplus_winrt) */ - }; - - - /// - /// The task handle type used to create a 'continuation task'. - /// - template - struct _ContinuationTaskHandle : - details::_PPLTaskHandle::_Type, - _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> - { - typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; - - typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; - typename details::_CopyableFunctor::type >::_Type _M_function; - - template - _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, - const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, - _ForwardedFunction&& _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) - : details::_PPLTaskHandle::_Type, - _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> - ::_PPLTaskHandle(_ContinuationImpl) - , _M_ancestorTaskImpl(_AncestorImpl) - , _M_function(std::forward<_ForwardedFunction>(_Func)) - { - this->_M_isTaskBasedContinuation = _IsTaskBased::value; - this->_M_continuationContext = _Context; - this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); - this->_M_inliningMode = _InliningMode; - } - - virtual ~_ContinuationTaskHandle() {} - - template - auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) - { - details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); - CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); - return _func(std::forward<_Arg>(_value)); - } - - void _Perform() const - { - _Continue(_IsTaskBased(), _TypeSelection()); - } - - void _SyncCancelAndPropagateException() const - { - if (_M_ancestorTaskImpl->_HasUserException()) - { - // If the ancestor encountered an exception, transfer the exception to the continuation - // This traverses down the tree to propagate the exception. - this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); - } - else - { - // If the ancestor was canceled, then your own execution should be canceled. - // This traverses down the tree to cancel it. - this->_M_pTask->_Cancel(true); - } - } - - // - // Overload 0-0: _InternalReturnType -> _TaskType - // - // This is a straight task continuation which simply invokes its target with the ancestor's completion argument - // - void _Continue(std::false_type, details::_TypeSelectorNoAsync) const - { - this->_M_pTask->_FinalizeAndRunContinuations( - _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); - } - - // - // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only under /ZW) - // or - // _InternalReturnType -> task<_TaskType> - // - // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation - // Depending on the output type, the right _AsyncInit gets invoked - // - void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const - { - typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - this->_M_pTask, - _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) - ); - } - -#if defined (__cplusplus_winrt) - // - // Overload 0-2: _InternalReturnType -> IAsyncAction^ - // - // This is a straight task continuation which returns an async action which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - this->_M_pTask, - ref new details::_IAsyncActionToAsyncOperationConverter( - _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); - } - - // - // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ - // - // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - this->_M_pTask, - ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); - } - - // - // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ - // - // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation - // - void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const - { - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; - - auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( - this->_M_pTask, - ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); - } - -#endif /* defined (__cplusplus_winrt) */ - - // - // Overload 1-0: task<_InternalReturnType> -> _TaskType - // - // This is an exception handling type of continuation which takes the task rather than the task's result. - // - void _Continue(std::true_type, details::_TypeSelectorNoAsync) const - { - typedef task<_InternalReturnType> _FuncInputType; - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - this->_M_pTask->_FinalizeAndRunContinuations( - _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); - } - - // - // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ - // or - // task<_TaskType> - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation or a task which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, - _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); - } - -#if defined (__cplusplus_winrt) - - // - // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async action which will be unwrapped for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, - ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); - } - - // - // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation with progress which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, - ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( - _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); - } - - // - // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ - // - // This is an exception handling type of continuation which takes the task rather than - // the task's result. It also returns an async operation with progress which will be unwrapped - // for continuation - // - void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const - { - // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. - task<_InternalReturnType> _ResultTask; - _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); - - typedef details::_GetProgressType::_Value _ProgressType; - - details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, - ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( - _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); - } -#endif /* defined (__cplusplus_winrt) */ - }; - - /// - /// Initializes a task using a lambda, function pointer or function object. - /// - template - void _TaskInitWithFunctor(const _Function& _Func) - { - typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; - - _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; - _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; - _M_Impl->_M_taskEventLogger._LogScheduleTask(false); - _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); - } - - /// - /// Initializes a task using a task completion event. - /// - void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) - { - _Event._RegisterTask(_M_Impl); - } - -#if defined (__cplusplus_winrt) - /// - /// Initializes a task using an asynchronous operation IAsyncOperation^ - /// - void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) - { - _M_Impl->_M_fFromAsync = true; - - // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit - // returns a completion could execute concurrently and the task must be fully initialized before that happens. - _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; - // Pass the shared pointer into _AsyncInit for storage in the Async Callback. - details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); - } - - /// - /// Initializes a task using an asynchronous operation IAsyncOperation^ - /// - void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) - { - _TaskInitAsyncOp(_AsyncOp); - } - - /// - /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress^ - /// - template - void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress::_Value, _Progress>^ _AsyncOp) - { - _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter::_Value, _Progress>(_AsyncOp)); - } -#endif /* defined (__cplusplus_winrt) */ - - /// - /// Initializes a task using a callable object. - /// - template - void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) - { - _TaskInitWithFunctor<_ReturnType, _Function>(_Func); - } - - /// - /// Initializes a task using a non-callable object. - /// - template - void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) - { - _TaskInitNoFunctor(_Param); - } - - template - auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType - { - if (!_M_Impl) - { - throw invalid_operation("then() cannot be called on a default constructed task."); - } - - details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); - auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); - return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); - } - - /// - /// The one and only implementation of then for void and non-void tasks. - /// - template - auto _ThenImpl(_Function&& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, - details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType - { - if (!_M_Impl) - { - throw invalid_operation("then() cannot be called on a default constructed task."); - } - - typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; - typedef details::_TaskTypeTraits _Async_type_traits; - typedef typename _Async_type_traits::_TaskRetType _TaskType; - - // - // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a - // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user - // explicitly passes the same token. - // - if (_PTokenState == nullptr) - { - if (_Function_type_traits::_Takes_task::value) - { - _PTokenState = details::_CancellationTokenState::_None(); - } - else - { - _PTokenState = _GetImpl()->_M_pTokenState; - } - } - - task<_TaskType> _ContinuationTask; - _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); - - _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); - _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; - _ContinuationTask._SetTaskCreationCallstack(_CreationStack); - - _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( - _GetImpl(), _ContinuationTask._GetImpl(), std::forward<_Function>(_Func), _ContinuationContext, _InliningMode)); - - return _ContinuationTask; - } - - // The underlying implementation for this task - typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; -}; - -/// -/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, -/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces -/// a result of type on successful completion. Tasks of type task<void> produce no result. -/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using -/// continuations(then), and join(when_all) and choice(when_any) patterns. -/// -/// -/// For more information, see . -/// -/**/ -template<> -class task -{ -public: - /// - /// The type of the result an object of this class produces. - /// - /**/ - typedef void result_type; - - /// - /// Constructs a task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task() : _M_unitTask() - { - // The default constructor should create a task with a nullptr impl. This is a signal that the - // task is not usable and should throw if any wait(), get() or then() APIs are used. - } - - /// - /// Constructs a task object. - /// - /// - /// The type of the parameter from which the task is to be constructed. - /// - /// - /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> - /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function - /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, - /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) - { - details::_ValidateTaskConstructorArgs(_Param); - - _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); - // Do not move the next line out of this function. It is important that _CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. - _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); - - _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); - } - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(const task& _Other): _M_unitTask(_Other._M_unitTask){} - - /// - /// Constructs a task object. - /// - /// - /// The source task object. - /// - /// - /// The default constructor for a task is only present in order to allow tasks to be used within containers. - /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then - /// will throw an invalid_argument exception when called on a default constructed task. - /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task - /// completion event is set. - /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the - /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. - /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface - /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created - /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, - /// and not when the lambda returns. - /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads - /// without the need for locks. - /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available - /// to Windows Store apps. - /// For more information, see . - /// - /**/ - task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(const task& _Other) - { - if (this != &_Other) - { - _M_unitTask = _Other._M_unitTask; - } - return *this; - } - - /// - /// Replaces the contents of one task object with another. - /// - /// - /// The source task object. - /// - /// - /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same - /// actual task as does. - /// - /**/ - task& operator=(task&& _Other) - { - if (this != &_Other) - { - _M_unitTask = std::move(_Other._M_unitTask); - } - return *this; - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); - } - - /// - /// Adds a continuation task to this task. - /// - /// - /// The type of the function object that will be invoked by this task. - /// - /// - /// The continuation function to execute when this task completes. This continuation function must take as input - /// a variable of either result_type or task<result_type>, where result_type is the type - /// of the result this task produces. - /// - /// - /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit - /// the token of its antecedent task. - /// - /// - /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store - /// style app. For more information, see task_continuation_context - /// - /// - /// The newly created continuation task. The result type of the returned task is determined by what returns. - /// - /// - /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available - /// to Windows Store apps. - /// For more information on how to use task continuations to compose asynchronous work, see . - /// - /**/ - template - __declspec(noinline) // Ask for no inlining so that the _CAPTURE_CALLSTACK gives us the expected result - auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - task_options _TaskOptions(_CancellationToken, _ContinuationContext); - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); - } - - /// - /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks - /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. - /// - /// - /// A task_status value which could be either completed or canceled. If the task encountered an exception - /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. - /// - /**/ - task_status wait() const - { - return _M_unitTask.wait(); - } - - /// - /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to - /// finish. This method does not return a value when called on a task with a result_type of void. - /// - /// - /// If the task is canceled, a call to get will throw a task_canceled exception. If the task - /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. - /// - /**/ - void get() const - { - _M_unitTask.get(); - } - - /// - /// Determines if the task is completed. - /// - /// - /// True if the task has completed, false otherwise. - /// - /// - /// The function returns true if the task is completed or canceled (with or without user exception). - /// - bool is_done() const - { - return _M_unitTask.is_done(); - } - - /// - /// Returns the scheduler for this task - /// - /// - /// A pointer to the scheduler - /// - scheduler_ptr scheduler() const - { - return _M_unitTask.scheduler(); - } - - /// - /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. - /// - /// - /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. - /// - /**/ - bool is_apartment_aware() const - { - return _M_unitTask.is_apartment_aware(); - } - - /// - /// Determines whether two task objects represent the same internal task. - /// - /// - /// true if the objects refer to the same underlying task, and false otherwise. - /// - /**/ - bool operator==(const task& _Rhs) const - { - return (_M_unitTask == _Rhs._M_unitTask); - } - - /// - /// Determines whether two task objects represent different internal tasks. - /// - /// - /// true if the objects refer to different underlying tasks, and false otherwise. - /// - /**/ - bool operator!=(const task& _Rhs) const - { - return !operator==(_Rhs); - } - - /// - /// Create an underlying task implementation. - /// - void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) - { - _M_unitTask._CreateImpl(_Ct, _Scheduler); - } - - /// - /// Return the underlying implementation for this task. - /// - const details::_Task_ptr::_Type & _GetImpl() const - { - return _M_unitTask._M_Impl; - } - - /// - /// Set the implementation of the task to be the supplied implementation. - /// - void _SetImpl(const details::_Task_ptr::_Type & _Impl) - { - _M_unitTask._SetImpl(_Impl); - } - - /// - /// Set the implementation of the task to be the supplied implementation using a move instead of a copy. - /// - void _SetImpl(details::_Task_ptr::_Type && _Impl) - { - _M_unitTask._SetImpl(std::move(_Impl)); - } - - /// - /// Sets a property determining whether the task is apartment aware. - /// - void _SetAsync(bool _Async = true) - { - _M_unitTask._SetAsync(_Async); - } - - /// - /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. - /// - void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) - { - _M_unitTask._SetTaskCreationCallstack(_callstack); - } - - /// - /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. - /// - template - auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, - details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType - { - // inherit from antecedent - auto _Scheduler = _GetImpl()->_GetScheduler(); - - return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); - } - -private: - template friend class task; - template friend class task_completion_event; - - /// - /// Initializes a task using a task completion event. - /// - void _TaskInitNoFunctor(task_completion_event& _Event) - { - _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); - } - -#if defined (__cplusplus_winrt) - /// - /// Initializes a task using an asynchronous action IAsyncAction^ - /// - void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) - { - _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); - } - - /// - /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ - /// - template - void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) - { - _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); - } -#endif /* defined (__cplusplus_winrt) */ - - /// - /// Initializes a task using a callable object. - /// - template - void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) - { - _M_unitTask._TaskInitWithFunctor(_Func); - } - - /// - /// Initializes a task using a non-callable object. - /// - template - void _TaskInitMaybeFunctor(_T & _Param, std::false_type) - { - _TaskInitNoFunctor(_Param); - } - - // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. - task _M_unitTask; -}; - -namespace details -{ - /// - /// The following type traits are used for the create_task function. - /// - -#if defined (__cplusplus_winrt) - // Unwrap functions for asyncOperations - template - _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); - - void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); - - template - _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); - - template - void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); -#endif /* defined (__cplusplus_winrt) */ - - // Unwrap task - template - _Ty _GetUnwrappedType(task<_Ty>); - - // Unwrap all supported types - template - auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); - // fallback - template - _Ty _GetUnwrappedReturnType(_Ty, ...); - - /// - /// _GetTaskType functions will retrieve task type T in task[T](Arg), - /// for given constructor argument Arg and its property "callable". - /// It will automatically unwrap argument to get the final return type if necessary. - /// - - // Non-Callable - template - _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); - - // Non-Callable - template - auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); - - // Callable - template - auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); - - // Special callable returns void - void _GetTaskType(std::function, std::true_type); - struct _BadArgType{}; - - template - auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); - - template - _BadArgType _FilterValidTaskType(_Ty _Param, ...); - - template - struct _TaskTypeFromParam - { - typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; - }; -} // namespace details - -/// -/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. -/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. -/// -/// -/// The type of the parameter from which the task is to be constructed. -/// -/// -/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event -/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. -/// -/// -/// A new task of type T, that is inferred from . -/// -/// -/// The first overload behaves like a task constructor that takes a single parameter. -/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not -/// allowed to pass in a different task object as the first parameter. -/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, -/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. -/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or -/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. -/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor -/// that returns either of those types, the created task will have type task<void>. -/// -/// -/// -/**/ -template -__declspec(noinline) -auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> -{ - static_assert(!std::is_same::_Type,details::_BadArgType>::value, -#if defined (__cplusplus_winrt) - "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" -#else /* defined (__cplusplus_winrt) */ - "incorrect argument for create_task; can be a callable object or a task_completion_event" -#endif /* defined (__cplusplus_winrt) */ - ); - details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); - task::_Type> _CreatedTask(_Param, _TaskOptions); - return _CreatedTask; -} - -/// -/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. -/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. -/// -/// -/// The type of the parameter from which the task is to be constructed. -/// -/// -/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event -/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. -/// -/// -/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. -/// -/// -/// A new task of type T, that is inferred from . -/// -/// -/// The first overload behaves like a task constructor that takes a single parameter. -/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not -/// allowed to pass in a different task object as the first parameter. -/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, -/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. -/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or -/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. -/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor -/// that returns either of those types, the created task will have type task<void>. -/// -/// -/// -/**/ -template -__declspec(noinline) -task<_ReturnType> create_task(const task<_ReturnType>& _Task) -{ - task<_ReturnType> _CreatedTask(_Task); - return _CreatedTask; -} - -#if defined (__cplusplus_winrt) -namespace details -{ - template - task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) - { - return task<_T>(op); - } - - template - task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) - { - return task<_T>(op); - } - - inline task _To_task_helper(Windows::Foundation::IAsyncAction^ op) - { - return task(op); - } - - template - task _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) - { - return task(op); - } - - template - class _ProgressDispatcherBase - { - public: - - virtual ~_ProgressDispatcherBase() - { - } - - virtual void _Report(const _ProgressType& _Val) = 0; - }; - - template - class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> - { - public: - - virtual ~_ProgressDispatcher() - { - } - - _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) - { - } - - virtual void _Report(const _ProgressType& _Val) - { - _M_ptr->_FireProgress(_Val); - } - - private: - - _ClassPtrType _M_ptr; - }; - class _ProgressReporterCtorArgType{}; -} // namespace details - -/// -/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound -/// to a particular asynchronous action or operation. -/// -/// -/// The payload type of each progress notification reported through the progress reporter. -/// -/// -/// This type is only available to Windows Store apps. -/// -/// -/**/ -template -class progress_reporter -{ - typedef std::shared_ptr> _PtrType; - -public: - - /// - /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. - /// - /// - /// The payload to report through a progress notification. - /// - /**/ - void report(const _ProgressType& _Val) const - { - _M_dispatcher->_Report(_Val); - } - - template - static progress_reporter _CreateReporter(_ClassPtrType _Ptr) - { - progress_reporter _Reporter; - details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); - _Reporter._M_dispatcher = _PtrType(_PDispatcher); - return _Reporter; - } - progress_reporter() {} - -private: - progress_reporter(details::_ProgressReporterCtorArgType); - - _PtrType _M_dispatcher; -}; - -namespace details -{ - // - // maps internal definitions for AsyncStatus and defines states that are not client visible - // - enum _AsyncStatusInternal - { - _AsyncCreated = -1, // externally invisible - // client visible states (must match AsyncStatus exactly) - _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, - _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, - _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, - _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, - // non-client visible internal states - _AsyncCancelPending, - _AsyncClosed, - _AsyncUndefined - }; - - // - // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results - // (which are progressively consumable between Start state and before Close is called) - // - enum _AsyncResultType - { - SingleResult = 0x0001, - MultipleResults = 0x0002 - }; - - // *************************************************************************** - // Template type traits and helpers for async production APIs: - // - - struct _ZeroArgumentFunctor { }; - struct _OneArgumentFunctor { }; - struct _TwoArgumentFunctor { }; - - // **************************************** - // CLASS TYPES: - - // ******************** - // TWO ARGUMENTS: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); - - // non-void arg: - template - _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); - - // ******************** - // ONE ARGUMENT: - - // non-void arg: - template - _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); - - // non-void arg: - template - void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); - - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); - - // ******************** - // ZERO ARGUMENT: - - // void arg: - template - void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); - - // void arg: - template - void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); - - // void arg: - template - _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); - - // **************************************** - // POINTER TYPES: - - // ******************** - // TWO ARGUMENTS: - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - template - _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); - - // ******************** - // ONE ARGUMENT: - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); - - template - _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); - - template - _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); - - // ******************** - // ZERO ARGUMENT: - - template - void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); - - template - void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); - - template - void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); - - template - void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); - - template - _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); - - template - _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); - - template - struct _FunctorArguments - { - static const size_t _Count = 0; - }; - - template<> - struct _FunctorArguments<_OneArgumentFunctor> - { - static const size_t _Count = 1; - }; - - template<> - struct _FunctorArguments<_TwoArgumentFunctor> - { - static const size_t _Count = 2; - }; - - template - struct _FunctorTypeTraits - { - typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; - static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; - - typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; - typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; - typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; - }; - - template - struct _FunctorTypeTraits<_T *> - { - typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; - static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; - - typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; - typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; - typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; - }; - - template - struct _ProgressTypeTraits - { - static const bool _TakesProgress = false; - typedef void _ProgressType; - }; - - template - struct _ProgressTypeTraits> - { - static const bool _TakesProgress = true; - typedef typename _T _ProgressType; - }; - - - template::_ArgumentCount> - struct _CAFunctorOptions - { - static const bool _TakesProgress = false; - static const bool _TakesToken = false; - typedef void _ProgressType; - }; - - template - struct _CAFunctorOptions<_T, 1> - { - private: - - typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; - - public: - - static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; - static const bool _TakesToken = !_TakesProgress; - typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; - }; - - template - struct _CAFunctorOptions<_T, 2> - { - private: - - typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; - - public: - - static const bool _TakesProgress = true; - static const bool _TakesToken = true; - typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; - }; - - ref class _Zip - { - }; - - // *************************************************************************** - // Async Operation Task Generators - // - - // - // Functor returns an IAsyncInfo - result needs to be wrapped in a task: - // - template - struct _SelectorTaskGenerator - { - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Progress), _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); - } - }; - - template - struct _SelectorTaskGenerator<_AsyncSelector, void> - { - template - static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(), _taskOptinos); - } - - template - static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Cts.get_token()), _taskOptinos); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Progress), _taskOptinos); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); - } - }; - - // - // Functor returns a result - it needs to be wrapped in a task: - // - template - struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> - { - -#pragma warning(push) -#pragma warning(disable: 4702) - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>( [=]() -> _ReturnType { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(); - }, _taskOptinos); - } -#pragma warning(pop) - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>( [=]() -> _ReturnType { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Cts.get_token()); - }, _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>( [=]() -> _ReturnType { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Progress); - }, _taskOptinos); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task<_ReturnType>( [=]() -> _ReturnType { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - return _Func(_Progress, _Cts.get_token()); - }, _taskOptinos); - } - }; - - template<> - struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> - { - template - static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task( [=]() { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - _Func(); - }, _taskOptinos); - } - - template - static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task( [=]() { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - _Func(_Cts.get_token()); - }, _taskOptinos); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task( [=]() { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - _Func(_Progress); - }, _taskOptinos); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - task_options _taskOptinos(_Cts.get_token()); - details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); - return task( [=]() { - _Task_generator_oversubscriber_t _Oversubscriber; - (_Oversubscriber); - _Func(_Progress, _Cts.get_token()); - }, _taskOptinos); - } - }; - - // - // Functor returns a task - the task can directly be returned: - // - template - struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> - { - template - static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(); - } - - template - static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Cts.get_token()); - } - - template - static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Progress); - } - - template - static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Progress, _Cts.get_token()); - } - }; - - template<> - struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> - { - template - static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(); - } - - template - static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Cts.get_token()); - } - - template - static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Progress); - } - - template - static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _Func(_Progress, _Cts.get_token()); - } - }; - - template - struct _TaskGenerator - { - }; - - template - struct _TaskGenerator<_Generator, false, false> - { - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); - } - }; - - template - struct _TaskGenerator<_Generator, true, false> - { - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); - } - }; - - template - struct _TaskGenerator<_Generator, false, true> - { - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); - } - }; - - template - struct _TaskGenerator<_Generator, true, true> - { - template - static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) - { - return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); - } - }; - - // *************************************************************************** - // Async Operation Attributes Classes - // - // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in - // a single container. An attribute class must define: - // - // Mandatory: - // ------------------------- - // - // _AsyncBaseType : The Windows Runtime interface which is being implemented. - // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. - // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. - // _ReturnType : The return type of the async construct (void for actions / non-void for operations) - // - // _TakesProgress : An indication as to whether or not - // - // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task - // - // Optional: - // ------------------------- - // - - template - struct _AsyncAttributes - { - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> - { - typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; - typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; - typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; - typedef typename _ReturnType _ReturnType; - typedef typename _ProgressType _ProgressType; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; - - static const bool _TakesProgress = true; - static const bool _TakesToken = _TakesToken; - - template - static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); - } - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> - { - typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; - typedef _Zip _ProgressDelegateType; - typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; - typedef typename _ReturnType _ReturnType; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; - - static const bool _TakesProgress = false; - static const bool _TakesToken = _TakesToken; - - template - static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); - } - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> - { - typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; - typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; - typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; - typedef void _ReturnType; - typedef typename _ProgressType _ProgressType; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; - - static const bool _TakesProgress = true; - static const bool _TakesToken = _TakesToken; - - template - static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); - } - }; - - template - struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> - { - typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; - typedef _Zip _ProgressDelegateType; - typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; - typedef void _ReturnType; - typedef typename _TaskTraits::_AsyncKind _AsyncKind; - typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; - typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; - - static const bool _TakesProgress = false; - static const bool _TakesToken = _TakesToken; - - template - static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) - { - return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); - } - }; - - template - struct _AsyncLambdaTypeTraits - { - typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; - typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; - typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; - - static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; - static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; - - typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; - typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; - }; - - // *************************************************************************** - // AsyncInfo (and completion) Layer: - // - - // - // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) - // - template < typename _Attributes, _AsyncResultType resultType = SingleResult > - ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType - { - internal: - - _AsyncInfoBase() : - _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), - _M_errorCode(S_OK), - _M_completeDelegate(nullptr), - _M_CompleteDelegateAssigned(0), - _M_CallbackMade(0) - { - _M_id = ::pplx::details::platform::GetNextAsyncId(); - } - - public: - virtual typename _Attributes::_ReturnType GetResults() - { - throw ::Platform::Exception::CreateException(E_UNEXPECTED); - } - - virtual property unsigned int Id - { - unsigned int get() - { - _CheckValidStateForAsyncInfoCall(); - - return _M_id; - } - - void set(unsigned int id) - { - _CheckValidStateForAsyncInfoCall(); - - if (id == 0) - { - throw ::Platform::Exception::CreateException(E_INVALIDARG); - } - else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); - } - - _M_id = id; - } - } - - virtual property Windows::Foundation::AsyncStatus Status - { - Windows::Foundation::AsyncStatus get() - { - _CheckValidStateForAsyncInfoCall(); - - _AsyncStatusInternal _Current = _M_currentStatus; - - // - // Map our internal cancel pending to canceled. This way "pending canceled" looks to the outside as "canceled" but - // can still transition to "completed" if the operation completes without acknowledging the cancellation request - // - switch(_Current) - { - case _AsyncCancelPending: - _Current = _AsyncCanceled; - break; - case _AsyncCreated: - _Current = _AsyncStarted; - break; - default: - break; - } - - return static_cast(_Current); - } - } - - virtual property Windows::Foundation::HResult ErrorCode - { - Windows::Foundation::HResult get() - { - _CheckValidStateForAsyncInfoCall(); - - Windows::Foundation::HResult _Hr; - _Hr.Value = _M_errorCode; - return _Hr; - } - } - - virtual property typename _Attributes::_ProgressDelegateType^ Progress - { - typename typename _Attributes::_ProgressDelegateType^ get() - { - return _GetOnProgress(); - } - - void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) - { - _PutOnProgress(_ProgressHandler); - } - } - - virtual void Cancel() - { - if (_TransitionToState(_AsyncCancelPending)) - { - _OnCancel(); - } - } - - virtual void Close() - { - if (_TransitionToState(_AsyncClosed)) - { - _OnClose(); - } - else - { - if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); - } - } - } - - virtual property typename _Attributes::_CompletionDelegateType^ Completed - { - typename _Attributes::_CompletionDelegateType^ get() - { - _CheckValidStateForDelegateCall(); - return _M_completeDelegate; - } - - void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) - { - _CheckValidStateForDelegateCall(); - // this delegate property is "write once" - if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) - { - _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); - _M_completeDelegate = _CompleteHandler; - // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below - // as perceived from _FireCompletion on another thread. - MemoryBarrier(); - if (_IsTerminalState()) - { - _FireCompletion(); - } - } - else - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); - } - } - } - - - protected private: - - // _Start - this is not externally visible since async operations "hot start" before returning to the caller - void _Start() - { - if (_TransitionToState(_AsyncStarted)) - { - _OnStart(); - } - else - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); - } - } - - - void _FireCompletion() - { - _TryTransitionToCompleted(); - - // we guarantee that completion can only ever be fired once - if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) - { - _M_completeDelegateContext._CallInContext([=] { - _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); - _M_completeDelegate = nullptr; - }); - } - } - - virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() - { - throw ::Platform::Exception::CreateException(E_UNEXPECTED); - } - - virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) - { - throw ::Platform::Exception::CreateException(E_UNEXPECTED); - } - - bool _TryTransitionToCompleted() - { - return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); - } - - bool _TryTransitionToCancelled() - { - return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); - } - - bool _TryTransitionToError(const HRESULT error) - { - _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); - return _TransitionToState(_AsyncStatusInternal::_AsyncError); - } - - // This method checks to see if the delegate properties can be - // modified in the current state and generates the appropriate - // error hr in the case of violation. - inline void _CheckValidStateForDelegateCall() - { - if (_M_currentStatus == _AsyncClosed) - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); - } - } - - // This method checks to see if results can be collected in the - // current state and generates the appropriate error hr in - // the case of a violation. - inline void _CheckValidStateForResultsCall() - { - _AsyncStatusInternal _Current = _M_currentStatus; - - if (_Current == _AsyncError) - { - throw ::Platform::Exception::CreateException(_M_errorCode); - } -#pragma warning(push) -#pragma warning(disable: 4127) // Conditional expression is constant - // single result illegal before transition to Completed or Cancelled state - if (resultType == SingleResult) -#pragma warning(pop) - { - if (_Current != _AsyncCompleted) - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); - } - } - // multiple results can be called after Start has been called and before/after Completed - else if (_Current != _AsyncStarted && - _Current != _AsyncCancelPending && - _Current != _AsyncCanceled && - _Current != _AsyncCompleted) - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); - } - } - - // This method can be called by derived classes periodically to determine - // whether the asynchronous operation should continue processing or should - // be halted. - inline bool _ContinueAsyncOperation() - { - return (_M_currentStatus == _AsyncStarted); - } - - // These two methods are used to allow the async worker implementation do work on - // state transitions. No real "work" should be done in these methods. In other words - // they should not block for a long time on UI timescales. - virtual void _OnStart() = 0; - virtual void _OnClose() = 0; - virtual void _OnCancel() = 0; - - private: - - // This method is used to check if calls to the AsyncInfo properties - // (id, status, errorcode) are legal in the current state. It also - // generates the appropriate error hr to return in the case of an - // illegal call. - inline void _CheckValidStateForAsyncInfoCall() - { - _AsyncStatusInternal _Current = _M_currentStatus; - if (_Current == _AsyncClosed) - { - throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); - } - else if (_Current == _AsyncCreated) - { - throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); - } - - } - - inline bool _TransitionToState(const _AsyncStatusInternal _NewState) - { - _AsyncStatusInternal _Current = _M_currentStatus; - - // This enforces the valid state transitions of the asynchronous worker object - // state machine. - switch(_NewState) - { - case _AsyncStatusInternal::_AsyncStarted: - if (_Current != _AsyncCreated) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCompleted: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCancelPending: - if (_Current != _AsyncStarted) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncCanceled: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncError: - if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) - { - return false; - } - break; - case _AsyncStatusInternal::_AsyncClosed: - if (!_IsTerminalState(_Current)) - { - return false; - } - break; - default: - return false; - break; - } - - // attempt the transition to the new state - // Note: if currentStatus_ == _Current, then there was no intervening write - // by the async work object and the swap succeeded. - _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( - _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), - _NewState, - static_cast(_Current))); - - // ICE returns the former state, if the returned state and the - // state we captured at the beginning of this method are the same, - // the swap succeeded. - return (_RetState == _Current); - } - - inline bool _IsTerminalState() - { - return _IsTerminalState(_M_currentStatus); - } - - inline bool _IsTerminalState(_AsyncStatusInternal status) - { - return (status == _AsyncError || - status == _AsyncCanceled || - status == _AsyncCompleted || - status == _AsyncClosed); - } - - private: - - _ContextCallback _M_completeDelegateContext; - typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; - _AsyncStatusInternal volatile _M_currentStatus; - HRESULT volatile _M_errorCode; - unsigned int _M_id; - long volatile _M_CompleteDelegateAssigned; - long volatile _M_CallbackMade; - }; - - // *************************************************************************** - // Progress Layer (optional): - // - - template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > - ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> - { - }; - - template< typename _Attributes, _AsyncResultType _ResultType> - ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> - { - internal: - - _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), - _M_progressDelegate(nullptr) - { - } - - virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override - { - _CheckValidStateForDelegateCall(); - return _M_progressDelegate; - } - - virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override - { - _CheckValidStateForDelegateCall(); - _M_progressDelegate = _ProgressHandler; - _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); - } - - void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) - { - if (_M_progressDelegate != nullptr) - { - _M_progressDelegateContext._CallInContext([=] { - _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); - }); - } - } - - private: - - _ContextCallback _M_progressDelegateContext; - typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; - }; - - template - ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> - { - }; - - // *************************************************************************** - // Task Adaptation Layer: - // - - // - // _AsyncTaskThunkBase provides a bridge between IAsync and task. - // - template - ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> - { - public: - - virtual _ReturnType GetResults() override - { - _CheckValidStateForResultsCall(); - return _M_task.get(); - } - - internal: - - typedef task<_ReturnType> _TaskType; - - _AsyncTaskThunkBase(const _TaskType& _Task) - : _M_task(_Task) - { - } - - _AsyncTaskThunkBase() - { - } - - protected: - - virtual void _OnStart() override - { - _M_task.then( [=](_TaskType _Antecedent) { - try - { - _Antecedent.get(); - } - catch(task_canceled&) - { - _TryTransitionToCancelled(); - } - catch(::Platform::Exception^ _Ex) - { - _TryTransitionToError(_Ex->HResult); - } - catch(...) - { - _TryTransitionToError(E_FAIL); - } - _FireCompletion(); - }); - } - - internal: - - _TaskType _M_task; - cancellation_token_source _M_cts; - }; - - template - ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> - { - internal: - - _AsyncTaskThunk(const _TaskType& _Task) : - _AsyncTaskThunkBase(_Task) - { - } - - _AsyncTaskThunk() - { - } - - protected: - - virtual void _OnClose() override - { - } - - virtual void _OnCancel() override - { - _M_cts.cancel(); - } - }; - - // *************************************************************************** - // Async Creation Layer: - // - template - ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk::_AsyncAttributes> - { - internal: - - typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; - typedef typename _AsyncTaskThunk<_Attributes> _Base; - typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; - - _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) - { - // Virtual call here is safe as the class is declared 'sealed' - _Start(); - } - - protected: - - // - // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, - // let the base thunk handle everything. - // - - virtual void _OnStart() override - { - // - // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, - // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. - // - _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); - _Base::_OnStart(); - } - - virtual void _OnCancel() override - { - _Base::_OnCancel(); - } - - private: - _TaskCreationCallstack _M_creationCallstack; - _Function _M_func; - }; -} // namespace details - -/// -/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is -/// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or -/// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. -/// -/// -/// The lambda or function object from which to create a Windows Runtime asynchronous construct. -/// -/// -/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an -/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. -/// -/// -/// The return type of the lambda determines whether the construct is an action or an operation. -/// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of -/// operations of TResult. -/// The lambda may also return a task<TResult> which encapsulates the asynchronous work within itself or is the continuation of -/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that -/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. -/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will -/// cause the creation of operations of TResult. -/// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and -/// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without -/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous -/// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that -/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the -/// asynchronous construct causes cancellation of those tasks. -/// If the body of the lambda or function object returns a result (and not a task<TResult>), the lambda will be executed -/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will -/// cause cancellation of the implicit task. -/// If the body of the lambda returns a task, the lambda executes inline, and by declaring the lambda to take an argument of type -/// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. -/// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on -/// the async operation or action produced.. -/// This function is only available to Windows Store apps. -/// -/// -/// -/// -/**/ -template -__declspec(noinline) -details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) -{ - static_assert(std::is_same::value, - "argument to create_async must be a callable object taking zero, one or two arguments"); - return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, _CAPTURE_CALLSTACK()); -} - -#endif /* defined (__cplusplus_winrt) */ - -namespace details -{ - // Helper struct for when_all operators to know when tasks have completed - template - struct _RunAllParam - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len, bool _SkipVector = false) - { - _M_numTasks = _Len; - if (!_SkipVector) - { - _M_vector._Result.resize(_Len); - } - } - - task_completion_event<_Unit_type> _M_completed; - _ResultHolder > _M_vector; - _ResultHolder<_Type> _M_mergeVal; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - }; - - template - struct _RunAllParam > - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len, bool _SkipVector = false) - { - _M_numTasks = _Len; - - if (!_SkipVector) - { - _M_vector.resize(_Len); - } - } - - task_completion_event<_Unit_type> _M_completed; - std::vector<_ResultHolder > > _M_vector; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - }; - - // Helper struct specialization for void - template<> - struct _RunAllParam<_Unit_type> - { - _RunAllParam() : _M_completeCount(0), _M_numTasks(0) - { - } - - void _Resize(size_t _Len) - { - _M_numTasks = _Len; - } - - task_completion_event<_Unit_type> _M_completed; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - }; - - inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) - { - if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) - { - cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); - _T.register_callback( [=](){ - _MergedSrc.cancel(); - }); - } - } - - template - void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) - { - if (_Task._GetImpl()->_IsCompleted()) - { - _Func(); - if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) - { - // Inline execute its direct continuation, the _ReturnTask - _PParam->_M_completed.set(_Unit_type()); - // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. - delete _PParam; - } - } - else - { - _ASSERTE(_Task._GetImpl()->_IsCanceled()); - if (_Task._GetImpl()->_HasUserException()) - { - // _Cancel will return false if the TCE is already canceled with or without exception - _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); - } - else - { - _PParam->_M_completed._Cancel(); - } - - if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) - { - delete _PParam; - } - } - } - - template - struct _WhenAllImpl - { - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) - { - _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - - auto _PParam = new _RunAllParam<_ElementType>(); - cancellation_token_source _MergedSource; - - // Step1: Create task completion event. - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { - return _PParam->_M_vector.Get(); - }, nullptr); - - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - _JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if( _Begin == _End ) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { - - auto _PParamCopy = _PParam; - auto _IndexCopy = _Index; - auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ - _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); - }; - - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - - _Index++; - } - } - - return _ReturnTask; - } - }; - - template - struct _WhenAllImpl, _Iterator> - { - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) - { - _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - - auto _PParam = new _RunAllParam>(); - cancellation_token_source _MergedSource; - - // Step1: Create task completion event. - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { - _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); - std::vector<_ElementType> _Result; - for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) - { - const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); - _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); - } - return _Result; - }, nullptr); - - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - _JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if( _Begin == _End ) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task> _ResultTask) { - - auto _PParamCopy = _PParam; - auto _IndexCopy = _Index; - auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { - _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); - }; - - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - - _Index++; - } - } - - return _ReturnTask; - } - }; - - template - struct _WhenAllImpl - { - static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) - { - _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - - auto _PParam = new _RunAllParam<_Unit_type>(); - cancellation_token_source _MergedSource; - - // Step1: Create task completion event. - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_MergedSource.get_token()); - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { - }, nullptr); - - // Step2: Combine and check tokens, and count elements in range. - if (_PTokenState) - { - _JoinAllTokens_Add(_MergedSource, _PTokenState); - _PParam->_Resize(static_cast(std::distance(_Begin, _End))); - } - else - { - size_t _TaskNum = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - _TaskNum++; - _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); - } - _PParam->_Resize(_TaskNum); - } - - // Step3: Check states of previous tasks. - if( _Begin == _End ) - { - _PParam->_M_completed.set(_Unit_type()); - delete _PParam; - } - else - { - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PTask->_Then([_PParam](task _ResultTask) { - auto _Func = [](){}; - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - } - } - - return _ReturnTask; - } - }; - - template - task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, - bool _OutputVectorFirst) - { - auto _PParam = new _RunAllParam<_ReturnType>(); - cancellation_token_source _MergedSource; - - // Step1: Create task completion event. - task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); - // The return task must be created before step 3 to enforce inline execution. - auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { - _ASSERTE(_PParam->_M_completeCount == 2); - auto _Result = _PParam->_M_vector.Get(); // copy by value - auto _mergeVal = _PParam->_M_mergeVal.Get(); - - if (_OutputVectorFirst == true) - { - _Result.push_back(_mergeVal); - } - else - { - _Result.insert(_Result.begin(), _mergeVal); - } - return _Result; - }, nullptr); - - // Step2: Combine and check tokens. - _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); - _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); - - // Step3: Check states of previous tasks. - _PParam->_Resize(2, true); - - if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - _VectorTask._Then([_PParam](task> _ResultTask) { - auto _PParamCopy = _PParam; - auto _Func = [_PParamCopy, &_ResultTask]() { - auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_vector.Set(_ResultLocal); - }; - - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { - auto _PParamCopy = _PParam; - auto _Func = [_PParamCopy, &_ResultTask]() { - auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_mergeVal.Set(_ResultLocal); - }; - - _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - - return _ReturnTask; - } -} // namespace details - -/// -/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// A task that completes successfully when all of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encountered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) - -> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encountered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) -{ - task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; - return when_all(_PTasks, _PTasks+2); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encountered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true)) -{ - return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encountered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false)) -{ - return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); -} - -/// -/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output -/// task will also be a task<void>. -/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator -/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. -/// -/// -/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, -/// if one is encountered, will be thrown if you call get() or wait() on that task. -/// -/// -/**/ -template -auto operator&&(const task> & _Lhs, const task> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) -{ - task> _PTasks[2] = {_Lhs, _Rhs}; - return when_all(_PTasks, _PTasks+2); -} - -namespace details -{ - // Helper struct for when_any operators to know when tasks have completed - template - struct _RunAnyParam - { - _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) - { - } - ~_RunAnyParam() - { - if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) - _M_exceptionRelatedToken->_Release(); - } - task_completion_event<_CompletionType> _M_Completed; - cancellation_token_source _M_cancellationSource; - _CancellationTokenState * _M_exceptionRelatedToken; - atomic_size_t _M_completeCount; - size_t _M_numTasks; - bool _M_fHasExplicitToken; - }; - - template - void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) - { - bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); - if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) - { - _Func(); - if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) - { - delete _PParam; - } - } - else - { - _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); - if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) - { - if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) - { - // This can only enter once. - _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; - _ASSERTE(_PParam->_M_exceptionRelatedToken); - // Deref token will be done in the _PParam destructor. - if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) - { - _PParam->_M_exceptionRelatedToken->_Reference(); - } - } - } - - if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) - { - // If no one has be completed so far, we need to make some final cancellation decision. - if (!_PParam->_M_Completed._IsTriggered()) - { - // If we already explicit token, we can skip the token join part. - if (!_PParam->_M_fHasExplicitToken) - { - if (_PParam->_M_exceptionRelatedToken) - { - _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); - } - else - { - // If haven't captured any exception token yet, there was no exception for all those tasks, - // so just pick a random token (current one) for normal cancellation. - _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); - } - } - // Do exception cancellation or normal cancellation based on whether it has stored exception. - _PParam->_M_Completed._Cancel(); - } - delete _PParam; - } - } - } - - template - struct _WhenAnyImpl - { - static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) - { - if( _Begin == _End ) - { - throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); - } - _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - auto _PParam = new _RunAnyParam, _CancellationTokenState *>>(); - - if (_PTokenState) - { - _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); - _PParam->_M_fHasExplicitToken = true; - } - - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); - task, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); - - // Keep a copy ref to the token source - auto _CancellationSource = _PParam->_M_cancellationSource; - - _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _Any_tasks_completed._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { - auto _PParamCopy = _PParam; // Dev10 - auto _IndexCopy = _Index; // Dev10 - auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); - }; - - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - - _Index++; - } - - // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. - return _Any_tasks_completed._Then([=](std::pair, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { - _ASSERTE(_Result.second); - if (!_PTokenState) - { - _JoinAllTokens_Add(_CancellationSource, _Result.second); - } - return _Result.first; - }, nullptr); - } - }; - - template - struct _WhenAnyImpl - { - static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) - { - if( _Begin == _End ) - { - throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); - } - - _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; - auto _PParam = new _RunAnyParam>(); - - if (_PTokenState) - { - _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); - _PParam->_M_fHasExplicitToken = true; - } - - task_options _Options(_TaskOptions); - _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); - task> _Any_tasks_completed(_PParam->_M_Completed, _Options); - - // Keep a copy ref to the token source - auto _CancellationSource = _PParam->_M_cancellationSource; - - _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); - size_t _Index = 0; - for (auto _PTask = _Begin; _PTask != _End; ++_PTask) - { - if (_PTask->is_apartment_aware()) - { - _Any_tasks_completed._SetAsync(); - } - - _PTask->_Then([_PParam, _Index](task _ResultTask) { - auto _PParamCopy = _PParam; // Dev10 - auto _IndexCopy = _Index; // Dev10 - auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }, _CancellationTokenState::_None()); - - _Index++; - } - - // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. - return _Any_tasks_completed._Then([=](std::pair _Result) -> size_t { - _ASSERTE(_Result.second); - if (!_PTokenState) - { - _JoinAllTokens_Add(_CancellationSource, _Result.second); - } - return _Result.first; - }, nullptr); - } - }; -} // namespace details - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result -/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void -/// the output is a task<size_t>, where the result is the index of the completing task. -/// -/// -/**/ -template -auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) - -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the input iterator. -/// -/// -/// The position of the first element in the range of elements to be combined into the resulting task. -/// -/// -/// The position of the first element beyond the range of elements to be combined into the resulting task. -/// -/// -/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting -/// task will receive the cancellation token of the task that causes it to complete. -/// -/// -/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result -/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void -/// the output is a task<size_t>, where the result is the index of the completing task. -/// -/// -/**/ -template -auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) - -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) -{ - typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; - return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); -} - -/// -/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -{ - auto _PParam = new details::_RunAnyParam>(); - - task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { - _ASSERTE(_Ret.second); - _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); - return _Ret.first; - }, nullptr); - - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { - // Dev10 compiler bug - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }; - - _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); - _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); - - return _ReturnTask; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) -{ - auto _PParam = new details::_RunAnyParam, details::_CancellationTokenState *>>(); - - task, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { - _ASSERTE(_Ret.second); - _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); - return _Ret.first; - }, nullptr); - - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - _Lhs._Then([_PParam](task> _ResultTask) { - // Dev10 compiler bug - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - auto _Result = _ResultTask._GetImpl()->_GetResult(); - _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }, details::_CancellationTokenState::_None()); - - - _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) - { - auto _PParamCopy = _PParam; - auto _Func = [&_ResultTask, _PParamCopy]() { - auto _Result = _ResultTask._GetImpl()->_GetResult(); - - std::vector<_ReturnType> _Vec; - _Vec.push_back(_Result); - _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }, details::_CancellationTokenState::_None()); - - return _ReturnTask; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template -auto operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(_Rhs || _Lhs) -{ - return _Rhs || _Lhs; -} - -/// -/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. -/// -/// -/// The type of the returned task. -/// -/// -/// The first task to combine into the resulting task. -/// -/// -/// The second task to combine into the resulting task. -/// -/// -/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, -/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task -/// will also be a task<void>. -/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence -/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> -/// and the other one is of type task<T>. -/// -/// -/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, -/// if any are encountered, will be thrown when you call get() or wait() on that task. -/// -/// -/**/ -template, typename _Pair = std::pair> -_Ty operator||(const task & _Lhs_arg, const task & _Rhs_arg) -{ - const _Ty& _Lhs = _Lhs_arg; - const _Ty& _Rhs = _Rhs_arg; - auto _PParam = new details::_RunAnyParam<_Pair>(); - - task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); - // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, - // So that _PParam can be used before it getting deleted. - auto _ReturnTask = _Any_task_completed._Then([=](_Pair _Ret) { - _ASSERTE(_Ret.second); - details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); - }, nullptr); - - if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) - { - _ReturnTask._SetAsync(); - } - - _PParam->_M_numTasks = 2; - auto _Continuation = [_PParam](_Ty _ResultTask) mutable { - // Dev10 compiler needs this. - auto _PParam1 = _PParam; - auto _Func = [&_ResultTask, _PParam1]() { - _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); - }; - _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); - }; - - _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); - _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); - - return _ReturnTask; -} - -template -task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_Ty> _Tce; - _Tce.set(_Param); - return create_task(_Tce, _TaskOptions); -} - -template -inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_Ty> _Tce; - _Tce.set(); - return create_task(_Tce, _TaskOptions); -} - -template -task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) -{ - task_completion_event<_TaskType> _Tce; - _Tce.set_exception(_Exception); - return create_task(_Tce, _TaskOptions); -} - -} // namespace Concurrency - -#pragma pop_macro("new") - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif -#pragma pack(pop) - -#endif // (defined(_MSC_VER) && (_MSC_VER >= 1800)) - -#ifndef _CONCRT_H -#ifndef _LWRCASE_CNCRRNCY -#define _LWRCASE_CNCRRNCY -// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace -// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. -namespace Concurrency {} -namespace concurrency = Concurrency; -#endif -#endif - -#endif // _PPLXTASKS_H + inline void _LogCancelTask(_Task_impl_base *) {} + struct _TaskEventLogger + { + void _LogScheduleTask(bool) {} + void _LogCancelTask() {} + void _LogWorkItemStarted() {} + void _LogWorkItemCompleted() {} + void _LogTaskExecutionStarted() {} + void _LogTaskExecutionCompleted() {} + void _LogTaskCompleted() {} + _TaskEventLogger(_Task_impl_base *) {} + }; + struct _TaskWorkItemRAIILogger + { + _TaskWorkItemRAIILogger(_TaskEventLogger &) {} + }; +#endif + + /// + /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler + /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. + /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for continuation tasks, it will be derived from + /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. + /// + /// + /// The result type of the _Task_impl. + /// + /// + /// The derived task handle class. The operator () needs to be implemented. + /// + /// + /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or _ContinuationTaskHandleBase. + /// + template + struct _PPLTaskHandle : _BaseTaskHandle + { + _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) + { + } + + virtual ~_PPLTaskHandle() + { + // Here is the sink of all task completion code paths + _M_pTask->_M_taskEventLogger._LogTaskCompleted(); + } + + virtual void invoke() const + { + // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled + // by the runtime. + _ASSERTE((bool)_M_pTask); + if (!_M_pTask->_TransitionedToStarted()) + { + static_cast(this)->_SyncCancelAndPropagateException(); + return; + } + + _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); + try + { + // All derived task handle must implement this contract function. + static_cast(this)->_Perform(); + } + catch(const task_canceled &) + { + _M_pTask->_Cancel(true); + } + catch(const _Interruption_exception &) + { + _M_pTask->_Cancel(true); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _M_pTask->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _M_pTask->_CancelWithException(std::current_exception()); + } + _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); + } + + // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. + // The return value should be automatically optimized by R-value ref. + _Task_ptr_base _GetTaskImplBase() const + { + return _M_pTask; + } + + typename _Task_ptr<_ReturnType>::_Type _M_pTask; + private: + _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator + }; + + /// + /// The base implementation of a first-class task. This class contains all the non-type specific + /// implementation details of the task. + /// + /**/ + struct _Task_impl_base + { + enum _TaskInternalState + { + // Tracks the state of the task, rather than the task collection on which the task is scheduled + _Created, + _Started, + _PendingCancel, + _Completed, + _Canceled + }; +// _M_taskEventLogger - 'this' : used in base member initializer list +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4355) +#endif + _Task_impl_base(_CancellationTokenState * _PTokenState, scheduler_ptr _Scheduler_arg) + : _M_TaskState(_Created), + _M_fFromAsync(false), _M_fUnwrappedTask(false), + _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), + _M_taskEventLogger(this) + { + // Set cancellation token + _M_pTokenState = _PTokenState; + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + _M_pTokenState->_Reference(); + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + + virtual ~_Task_impl_base() + { + _ASSERTE(_M_pTokenState != nullptr); + if (_M_pTokenState != _CancellationTokenState::_None()) + { + _M_pTokenState->_Release(); + } + } + + task_status _Wait() + { + bool _DoWait = true; + +#if defined (__cplusplus_winrt) + if (_IsNonBlockingThread()) + { + // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal + // if task has not been completed. + if (!_IsCompleted() && !_IsCanceled()) + { + throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); + } + else + { + // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation + // needs to be marshaled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM + // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which + // task based continuations are wont to do), waiting on the task group results in on the chore that is making this + // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on + // if it has finished execution (which means now we are on the inline synchronous callback). + _DoWait = false; + } + } +#endif /* defined (__cplusplus_winrt) */ + if (_DoWait) + { + // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The + // async operation will take place on a thread in the appropriate apartment Simply wait for the completed + // event to be set. + if (_M_fFromAsync) + { + _M_TaskCollection._Wait(); + } + else + { + // Wait on the task collection to complete. The task collection is guaranteed to still be + // valid since the task must be still within scope so that the _Task_impl_base destructor + // has not yet been called. This call to _Wait potentially inlines execution of work. + try + { + // Invoking wait on a task collection resets the state of the task collection. This means that + // if the task collection itself were canceled, or had encountered an exception, only the first + // call to wait will receive this status. However, both cancellation and exceptions flowing through + // tasks set state in the task impl itself. + + // When it returns canceled, either work chore or the cancel thread should already have set task's state + // properly -- canceled state or completed state (because there was no interruption point). + // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. + _M_TaskCollection._RunAndWait(); + } + catch(details::_Interruption_exception&) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(task_canceled&) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(_E); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } + + // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task + // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must + // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; + // however, this takes the tact of simply waiting upon the completion signal. + if (_M_fUnwrappedTask) + { + _M_TaskCollection._Wait(); + } + } + } + + if (_HasUserException()) + { + _M_exceptionHolder->_RethrowUserException(); + } + else if (_IsCanceled()) + { + return canceled; + } + _ASSERTE(_IsCompleted()); + return completed; + } + + /// + /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. + /// + /// + /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task + /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at + /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could + /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. + /// + /// + /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. + /// + /// + /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when + /// _UserException is set to true. + /// + /// + /// The exception holder that represents the exception. Only valid when _UserException is set to true. + /// + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; + + bool _Cancel(bool _SynchronousCancel) + { + // Send in a dummy value for exception. It is not used when the first parameter is false. + return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); + } + + bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) + { + // This task was canceled because an ancestor task encountered an exception. + return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); + } + +#if defined (__cplusplus_winrt) + bool _CancelWithException(::Platform::Exception^ _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } +#endif /* defined (__cplusplus_winrt) */ + + bool _CancelWithException(const std::exception_ptr& _Exception) + { + // This task was canceled because the task body encountered an exception. + _ASSERTE(!_HasUserException()); + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); + } + + void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) + { + _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState)); + + auto _CancellationCallback = [_WeakPtr](){ + // Taking ownership of the task prevents dead lock during destruction + // if the destructor waits for the cancellations to be finished + auto _task = _WeakPtr.lock(); + if (_task != nullptr) + _task->_Cancel(false); + }; + + _M_pRegistration = new details::_CancellationTokenCallback(_CancellationCallback); + _M_pTokenState->_RegisterCallback(_M_pRegistration); + } + + void _DeregisterCancellation() + { + if (_M_pRegistration != nullptr) + { + _M_pTokenState->_DeregisterCallback(_M_pRegistration); + _M_pRegistration->_Release(); + _M_pRegistration = nullptr; + } + } + + bool _IsCreated() + { + return (_M_TaskState == _Created); + } + + bool _IsStarted() + { + return (_M_TaskState == _Started); + } + + bool _IsPendingCancel() + { + return (_M_TaskState == _PendingCancel); + } + + bool _IsCompleted() + { + return (_M_TaskState == _Completed); + } + + bool _IsCanceled() + { + return (_M_TaskState == _Canceled); + } + + bool _HasUserException() + { + return static_cast(_M_exceptionHolder); + } + + const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() + { + _ASSERTE(_HasUserException()); + return _M_exceptionHolder; + } + + bool _IsApartmentAware() + { + return _M_fFromAsync; + } + + void _SetAsync(bool _Async = true) + { + _M_fFromAsync = _Async; + } + + _TaskCreationCallstack _GetTaskCreationCallstack() + { + return _M_pTaskCreationCallstack; + } + + void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) + { + _M_pTaskCreationCallstack = _Callstack; + } + + /// + /// Helper function to schedule the task on the Task Collection. + /// + /// + /// The task chore handle that need to be executed. + /// + /// + /// The inlining scheduling policy for current _PTaskHandle. + /// + void _ScheduleTask(_UnrealizedChore_t * _PTaskHandle, _TaskInliningMode_t _InliningMode) + { + try + { + _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); + } + catch(const task_canceled &) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _ASSERTE(_IsCanceled()); + } + catch(const _Interruption_exception &) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _ASSERTE(false); + } + catch(...) + { + // The exception could have come from two places: + // 1. From the chore body, so it already should have been caught and canceled. + // In this case swallow the exception. + // 2. From trying to actually schedule the task on the scheduler. + // In this case cancel the task with the current exception, otherwise the + // task will never be signaled leading to deadlock when waiting on the task. + if (!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + } + } + + /// + /// Function executes a continuation. This function is recorded by a parent task implementation + /// when a continuation is created in order to execute later. + /// + /// + /// The continuation task chore handle that need to be executed. + /// + /**/ + void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); + if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) + { + if (_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _ImplBase->_Cancel(true); + } + } + else + { + // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled + // (with or without a user exception). + _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); + _ASSERTE(!_ImplBase->_IsCanceled()); + return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); + } + + // If the handle is not scheduled, we need to manually delete it. + delete _PTaskHandle; + } + + // Schedule a continuation to run + void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) + { + + _M_taskEventLogger._LogScheduleTask(true); + // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) + if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) + { + // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, + // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce + // the cost of marshaling. + // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. + if (_PTaskHandle->_M_inliningMode != details::_ForceInline) + { + _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline; + } + _ScheduleFuncWithAutoInline([_PTaskHandle]() { + // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. + // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. + auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); + if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) + { + _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline); + } + else + { + // + // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle + // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: + // + // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into + // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will + // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). + // + try + { + // Dev10 compiler needs this! + auto _PTaskHandle1 = _PTaskHandle; + _PTaskHandle->_M_continuationContext._CallInContext( [_PTaskHandle1, _TaskImplPtr](){ + _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline); + }); + } +#if defined (__cplusplus_winrt) + catch(::Platform::Exception^ _E) + { + _TaskImplPtr->_CancelWithException(_E); + } +#endif /* defined (__cplusplus_winrt) */ + catch(...) + { + _TaskImplPtr->_CancelWithException(std::current_exception()); + } + } + }, _PTaskHandle->_M_inliningMode); + } + else + { + _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); + } + } + + /// + /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation + /// if the task has completed or append it to a list of functions to execute when the task actually does complete. + /// + /// + /// The input type of the task. + /// + /// + /// The output type of the task. + /// + /**/ + void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; + + // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. + // Otherwise, add it to the list of pending continuations + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) + { + _Do = _Schedule; + } + else if (_IsCanceled()) + { + if (_HasUserException()) + { + _Do = _CancelWithException; + } + else + { + _Do = _Cancel; + } + } + else + { + // chain itself on the continuation chain. + _PTaskHandle->_M_next = _M_Continuations; + _M_Continuations = _PTaskHandle; + } + } + + // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of + // async tasks may execute inline. + switch (_Do) + { + case _Schedule: + { + _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); + break; + } + case _Cancel: + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _PTaskHandle->_GetTaskImplBase()->_Cancel(true); + + delete _PTaskHandle; + break; + } + case _CancelWithException: + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + + delete _PTaskHandle; + break; + } + case _Nothing: + default: + // In this case, we have inserted continuation to continuation chain, + // nothing more need to be done, just leave. + break; + } + } + + void _RunTaskContinuations() + { + // The link list can no longer be modified at this point, + // since all following up continuations will be scheduled by themselves. + _ContinuationList _Cur = _M_Continuations, _Next; + _M_Continuations = nullptr; + while (_Cur) + { + // Current node might be deleted after running, + // so we must fetch the next first. + _Next = _Cur->_M_next; + _RunContinuation(_Cur); + _Cur = _Next; + } + } + +#if defined (__cplusplus_winrt) + static bool _IsNonBlockingThread() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + // + // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. + // + if (SUCCEEDED(hr)) + { + switch(_AptType) + { + case APTTYPE_STA: + case APTTYPE_MAINSTA: + return true; + break; + case APTTYPE_NA: + switch(_AptTypeQualifier) + { + // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed + // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting + // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the + // thread out of circulation for a while. + case APTTYPEQUALIFIER_NA_ON_STA: + case APTTYPEQUALIFIER_NA_ON_MAINSTA: + return true; + break; + } + break; + } + } + +#if _UITHREADCTXT_SUPPORT + // This method is used to throw an exception in _Wait() if called within STA. We + // want the same behavior if _Wait is called on the UI thread. + if (SUCCEEDED(CaptureUiThreadContext(nullptr))) + { + return true; + } +#endif /* _UITHREADCTXT_SUPPORT */ + + return false; + } + + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, + Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + // This method is invoked either when a task is created from an existing async operation or + // when a lambda that creates an async operation executes. + + // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on + // the IAsyncInfo object will be released when all ^references to the operation go out of scope. + + // This assertion uses the existence of taskcollection to determine if the task was created from an event. + // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection + // when a custom scheduler is used. + // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); + + // Pass the shared_ptr by value into the lambda instead of using 'this'. + _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>( + [_OuterTask](Windows::Foundation::IAsyncOperation::_Value>^ _Operation, Windows::Foundation::AsyncStatus _Status) mutable + { + if (_Status == Windows::Foundation::AsyncStatus::Canceled) + { + _OuterTask->_Cancel(true); + } + else if (_Status == Windows::Foundation::AsyncStatus::Error) + { + _OuterTask->_CancelWithException(::Platform::Exception::ReCreateException(static_cast(_Operation->ErrorCode.Value))); + } + else + { + _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed); + _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); + } + + // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could + // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold + // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from + // it using the Windows Runtime Async APIs causes a sharing violation. + // Using const_cast is the workaround for failed mutable keywords + const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); + }); + _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); + } +#endif /* defined (__cplusplus_winrt) */ + + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) + { + _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); + + // + // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the + // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation + // off the inner task which does the appropriate funneling to the outer one. We use _Then instead of then to prevent + // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless + // of whether or not the _OuterTask task is canceled. + // + _UnwrappedTask._Then([_OuterTask] (task<_InternalReturnType> _AncestorTask) { + + if (_AncestorTask._GetImpl()->_IsCompleted()) + { + _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); + } + else + { + _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled()); + if (_AncestorTask._GetImpl()->_HasUserException()) + { + // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. + // Instead, it is the enclosing task. + _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); + } + else + { + _OuterTask->_Cancel(true); + } + } + }, nullptr, details::_DefaultAutoInline); + + } + + scheduler_ptr _GetScheduler() const + { + return _M_TaskCollection._GetScheduler(); + } + + // Tracks the internal state of the task + std::atomic<_TaskInternalState> _M_TaskState; + // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an + // async operation or async action that is unwrapped by the runtime. + bool _M_fFromAsync; + // Set to true when a continuation unwraps a task or async operation. + bool _M_fUnwrappedTask; + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + + ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec; + + // The cancellation token state. + _CancellationTokenState * _M_pTokenState; + + // The registration on the token. + _CancellationTokenRegistration * _M_pRegistration; + + typedef _ContinuationTaskHandleBase * _ContinuationList; + _ContinuationList _M_Continuations; + + // The async task collection wrapper + ::pplx::details::_TaskCollection_t _M_TaskCollection; + + // Callstack for function call (constructor or .then) that created this task impl. + _TaskCreationCallstack _M_pTaskCreationCallstack; + + _TaskEventLogger _M_taskEventLogger; + private: + // Must not be copied by value: + _Task_impl_base(const _Task_impl_base&); + _Task_impl_base const & operator=(_Task_impl_base const&); + }; + +#if PPLX_TASK_ASYNC_LOGGING + inline void _TaskEventLogger::_LogTaskCompleted() + { + if (_M_scheduled) + { + ::Windows::Foundation::AsyncStatus _State; + if (_M_task->_IsCompleted()) + _State = ::Windows::Foundation::AsyncStatus::Completed; + else if (_M_task->_HasUserException()) + _State = ::Windows::Foundation::AsyncStatus::Error; + else + _State = ::Windows::Foundation::AsyncStatus::Canceled; + + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); + } + } + } +#endif + + /// + /// The implementation of a first-class task. This structure contains the task group used to execute + /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr + /// member of the the public task class, so its destruction is handled automatically. + /// + /// + /// The result type of this task. + /// + /**/ + template + struct _Task_impl : public _Task_impl_base + { +#if defined (__cplusplus_winrt) + typedef Windows::Foundation::IAsyncOperation::_Value> _AsyncOperationType; +#endif // defined(__cplusplus_winrt) + _Task_impl(_CancellationTokenState * _Ct, scheduler_ptr _Scheduler_arg) + : _Task_impl_base(_Ct, _Scheduler_arg) + { +#if defined (__cplusplus_winrt) + _M_unwrapped_async_op = nullptr; +#endif /* defined (__cplusplus_winrt) */ + } + + virtual ~_Task_impl() + { + // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause + // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. + _DeregisterCancellation(); + } + + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder_arg) + { + bool _RunContinuations = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + if (_UserException) + { + _ASSERTE(_SynchronousCancel && !_IsCompleted()); + // If the state is _Canceled, the exception has to be coming from an ancestor. + _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor); + + // We should not be canceled with an exception more than once. + _ASSERTE(!_HasUserException()); + + // Mark _PropagatedFromAncestor as used. + (void)_PropagatedFromAncestor; + + if (_M_TaskState == _Canceled) + { + // If the task has finished canceling there should not be any continuation records in the array. + return false; + } + else + { + _ASSERTE(_M_TaskState != _Completed); + _M_exceptionHolder = _ExceptionHolder_arg; + } + } + else + { + // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel + // which is to say, cancellation is already initiated, so return early. + if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) + { + _ASSERTE(!_IsCompleted() || !_HasUserException()); + return false; + } + _ASSERTE(!_SynchronousCancel || !_HasUserException()); + } + + if (_SynchronousCancel) + { + // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() + _M_TaskState = _Canceled; + // Cancellation completes the task, so all dependent tasks must be run to cancel them + // They are canceled when they begin running (see _RunContinuation) and see that their + // ancestor has been canceled. + _RunContinuations = true; + } + else + { + _ASSERTE(!_UserException); + + if (_IsStarted()) + { +#if defined (__cplusplus_winrt) + if (_M_unwrapped_async_op != nullptr) + { + // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. + _M_unwrapped_async_op->Cancel(); + } +#endif /* defined (__cplusplus_winrt) */ + _M_TaskCollection._Cancel(); + } + + // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). + // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from + // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. + _M_TaskState = _PendingCancel; + + _M_taskEventLogger._LogCancelTask(); + } + + + } + + // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. + if (_RunContinuations) + { + _M_TaskCollection._Complete(); + + if (_M_Continuations) + { + // Scheduling cancellation with automatic inlining. + _ScheduleFuncWithAutoInline([=](){ _RunTaskContinuations(); }, details::_DefaultAutoInline); + } + } + return true; + } + + void _FinalizeAndRunContinuations(_ReturnType _Result) + { + _M_Result.Set(_Result); + + { + // + // Hold this lock to ensure continuations being concurrently either get added + // to the _M_Continuations vector or wait for the result + // + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + + // A task could still be in the _Created state if it was created with a task_completion_event. + // It could also be in the _Canceled state for the same reason. + _ASSERTE(!_HasUserException() && !_IsCompleted()); + if (_IsCanceled()) + { + return; + } + + // Always transition to "completed" state, even in the face of unacknowledged pending cancellation + _M_TaskState = _Completed; + } + _M_TaskCollection._Complete(); + _RunTaskContinuations(); + } + + // + // This method is invoked when the starts executing. The task returns early if this method returns true. + // + bool _TransitionedToStarted() + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. + _ASSERTE(!_IsCanceled()); + if (_IsPendingCancel()) + return false; + + _ASSERTE(_IsCreated()); + _M_TaskState = _Started; + return true; + } + +#if defined (__cplusplus_winrt) + void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec); + // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. + if (_IsPendingCancel()) + { + _ASSERTE(!_IsCanceled()); + _AsyncOp->Cancel(); + } + else + { + _M_unwrapped_async_op = _AsyncOp; + } + } +#endif /* defined (__cplusplus_winrt) */ + + // Return true if the task has reached a terminal state + bool _IsDone() + { + return _IsCompleted() || _IsCanceled(); + } + + _ReturnType _GetResult() + { + return _M_Result.Get(); + } + + _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. +#if defined (__cplusplus_winrt) + _AsyncOperationType^ _M_unwrapped_async_op; +#endif /* defined (__cplusplus_winrt) */ + }; + + template + struct _Task_completion_event_impl + { + private: + _Task_completion_event_impl(const _Task_completion_event_impl&); + _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); + + public: + + typedef std::vector::_Type> _TaskList; + + _Task_completion_event_impl() : + _M_fHasValue(false), _M_fIsCanceled(false) + { + } + + bool _HasUserException() + { + return _M_exceptionHolder != nullptr; + } + + ~_Task_completion_event_impl() + { + for( auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt ) + { + _ASSERTE(!_M_fHasValue && !_M_fIsCanceled); + // Cancel the tasks since the event was never signaled or canceled. + (*_TaskIt)->_Cancel(true); + } + } + + // We need to protect the loop over the array, so concurrent_vector would not have helped + _TaskList _M_tasks; + ::pplx::extensibility::critical_section_t _M_taskListCritSec; + _ResultHolder<_ResultType> _M_value; + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + std::atomic _M_fHasValue; + std::atomic _M_fIsCanceled; + }; + + // Utility method for dealing with void functions + inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function& _Func) + { + return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; + } + + template + std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) + { + return [=](_Unit_type) -> _Type { return _Func(); }; + } + + template + std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function& _Func) + { + return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; + } + + inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function& _Func) + { + return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; + } +} // namespace details + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// The result type of this task_completion_event class. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template +class task_completion_event +{ +public: + /// + /// Constructs a task_completion_event object. + /// + /**/ + task_completion_event() + : _M_Impl(std::make_shared>()) + { + } + + /// + /// Sets the task completion event. + /// + /// + /// The result to set this event with. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. + if (_IsTriggered()) + { + return false; + } + + _TaskList _Tasks; + bool _RunContinuations = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + if (!_IsTriggered()) + { + _M_Impl->_M_value.Set(_Result); + _M_Impl->_M_fHasValue = true; + + _Tasks.swap(_M_Impl->_M_tasks); + _RunContinuations = true; + } + } + + if (_RunContinuations) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // If current task was canceled by a cancellation_token, it would be in cancel pending state. + if ((*_TaskIt)->_IsPendingCancel()) + (*_TaskIt)->_Cancel(true); + else + { + // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all + // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we + // need to run continuations after the lock is released. + (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + } + if (_M_Impl->_HasUserException()) + { + _M_Impl->_M_exceptionHolder.reset(); + } + return true; + } + + return false; + } + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK()); + } + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK()); + } + + /// + /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + bool _Cancel() const + { + // Cancel with the stored exception if one exists. + return _CancelInternal(); + } + + /// + /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled + /// with the same exception. + /// + template + bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + bool _Canceled; + if(_StoreException(_ExHolder, _SetExceptionAddressHint)) + { + _Canceled = _CancelInternal(); + _ASSERTE(_Canceled); + } + else + { + _Canceled = false; + } + return _Canceled; + } + + /// + /// Internal method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + template + bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack ()) const + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + if (!_IsTriggered() && !_M_Impl->_HasUserException()) + { + // Create the exception holder only if we have ensured there we will be successful in setting it onto the + // task completion event. Failing to do so will result in an unobserved task exception. + _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); + return true; + } + return false; + } + + /// + /// Tests whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; + } + +private: + + static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) + { + return _ExHolder; + } + + static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) + { + return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); + } + + + template friend class task; // task can register itself with the event by calling the private _RegisterTask + template friend class task_completion_event; + + typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; + + /// + /// Cancels the task_completion_event. + /// + bool _CancelInternal() const + { + // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal + // will never be invoked if the task completion event has been set. + _ASSERTE(!_M_Impl->_M_fHasValue); + if (_M_Impl->_M_fIsCanceled) + { + return false; + } + + _TaskList _Tasks; + bool _Cancel = false; + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + _ASSERTE(!_M_Impl->_M_fHasValue); + if (!_M_Impl->_M_fIsCanceled) + { + _M_Impl->_M_fIsCanceled = true; + _Tasks.swap(_M_Impl->_M_tasks); + _Cancel = true; + } + } + + bool _UserException = _M_Impl->_HasUserException(); + + if (_Cancel) + { + for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) + { + // Need to call this after the lock is released. See comments in set(). + if (_UserException) + { + (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else + { + (*_TaskIt)->_Cancel(true); + } + } + } + return _Cancel; + } + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) + { + ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec); + + //If an exception was already set on this event, then cancel the task with the stored exception. + if(_M_Impl->_HasUserException()) + { + _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else if (_M_Impl->_M_fHasValue) + { + _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } + else + { + _M_Impl->_M_tasks.push_back(_TaskParam); + } + } + + std::shared_ptr> _M_Impl; +}; + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template<> +class task_completion_event +{ +public: + /// + /// Sets the task completion event. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent.set(details::_Unit_type()); + } + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK()); + } + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK intrinsic gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _M_unitEvent._Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK()); + } + + /// + /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + _M_unitEvent._Cancel(); + } + + /// + /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled + /// with the same exception. + /// + void _Cancel(const std::shared_ptr& _ExHolder) const + { + _M_unitEvent._Cancel(_ExHolder); + } + + /// + /// Method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + bool _StoreException(const std::shared_ptr& _ExHolder) const + { + return _M_unitEvent._StoreException(_ExHolder); + } + + /// + /// Test whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_unitEvent._IsTriggered(); + } + +private: + template friend class task; // task can register itself with the event by calling the private _RegisterTask + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(details::_Task_ptr::_Type _TaskParam) + { + _M_unitEvent._RegisterTask(_TaskParam); + } + + // The void event contains an event a dummy type so common code can be used for events with void and non-void results. + task_completion_event _M_unitEvent; +}; + +namespace details +{ + // + // Compile-time validation helpers + // + + // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. + // + // Anything callable is fine + template + auto _IsValidTaskCtor(_Ty _Param, int,int,int,int) -> decltype(_Param(), std::true_type()); + +#if defined (__cplusplus_winrt) + // Anything that has GetResults is fine: this covers all async operations + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int,...) -> decltype(_Param->GetResults(), std::true_type()); +#endif + + // Allow parameters with set: this covers task_completion_event + template + auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); + + template + auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type()); + + // All else is invalid + template + std::false_type _IsValidTaskCtor(_Ty _Param, ...); + + template + void _ValidateTaskConstructorArgs(_Ty _Param) + { + static_assert(std::is_same(_Param,0,0,0,0)),std::true_type>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for task constructor; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); +#if defined (__cplusplus_winrt) + static_assert(!(std::is_same<_Ty,_ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), + "incorrect template argument for task; consider using the return type of the async operation"); +#endif /* defined (__cplusplus_winrt) */ + } + +#if defined (__cplusplus_winrt) + // Helpers for create_async validation + // + // A parameter lambda taking no arguments is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type()); + + // A parameter lambda taking an cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> decltype(_Param(cancellation_token::none()), std::true_type()); + + // A parameter lambda taking a progress report argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); + + // A parameter lambda taking a progress report and a cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type()); + + // All else is invalid + template + static std::false_type _IsValidCreateAsync(_Ty _Param, ...); +#endif /* defined (__cplusplus_winrt) */ + +/// +/// A helper class template that makes only movable functions be able to be passed to std::function +/// + template + struct _NonCopyableFunctorWrapper + { + template, + typename std::decay<_Tx>::type>::value>::type> + explicit _NonCopyableFunctorWrapper(_Tx&& f) + : _M_functor{std::make_shared<_Ty>(std::forward<_Tx>(f))} + {} + + template + auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + template + auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...)) + { + return _M_functor->operator()(std::forward<_Args>(args)...); + } + + std::shared_ptr<_Ty> _M_functor; + }; + + template + struct _CopyableFunctor + { + typedef _Ty _Type; + }; + + template + struct _CopyableFunctor<_Ty, typename std::enable_if< + std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type> + { + typedef _NonCopyableFunctorWrapper<_Ty> _Type; + }; +} +/// +/// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a +/// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. +/// +template +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) + { + return details::_MakeUnitToTFunc<_OutType>(_Func); + } +}; + +template +class _Continuation_func_transformer<_InType, void> +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) + { + return details::_MakeTToUnitFunc<_InType>(_Func); + } +}; + +template<> +class _Continuation_func_transformer +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) + { + return details::_MakeUnitToUnitFunc(_Func); + } +}; + +// A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used +// to substitute for void). This is to minimize the special handling required for 'void'. +template +class _Init_func_transformer +{ +public: + static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) + { + return _Func; + } +}; + +template<> +class _Init_func_transformer +{ +public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) + { + return details::_MakeVoidToUnitFunc(_Func); + } +}; + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// The result type of this task. +/// +/// +/// For more information, see . +/// +/**/ +template +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef _ReturnType result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_Impl(nullptr) + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token cancellation_token::none(). + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param) + { + task_options _TaskOptions; + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(PPLX_CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The task options include cancellation token, scheduler etc + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options &_TaskOptions) + { + details::_ValidateTaskConstructorArgs<_ReturnType,_Ty>(_Param); + + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : PPLX_CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other): _M_Impl(_Other._M_Impl) {} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other): _M_Impl(std::move(_Other._M_Impl)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_Impl = _Other._M_Impl; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_Impl = std::move(_Other._M_Impl); + } + return *this; + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions; + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The task options include cancellation token, scheduler and continuation context. By default the former 3 + /// options are inherited from the antecedent task + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions); + } + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + if (!_M_Impl) + { + throw invalid_operation("wait() cannot be called on a default constructed task."); + } + + return _M_Impl->_Wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// The result of the task. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + _ReturnType get() const + { + if (!_M_Impl) + { + throw invalid_operation("get() cannot be called on a default constructed task."); + } + + if (_M_Impl->_Wait() == canceled) + { + throw task_canceled(); + } + + return _M_Impl->_GetResult(); + } + + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + if (!_M_Impl) + { + throw invalid_operation("is_done() cannot be called on a default constructed task."); + } + + return _M_Impl->_IsDone(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + scheduler_ptr scheduler() const + { + if (!_M_Impl) + { + throw invalid_operation("scheduler() cannot be called on a default constructed task."); + } + + return _M_Impl->_GetScheduler(); + } + + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + if (!_M_Impl) + { + throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); + } + return _M_Impl->_IsApartmentAware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task<_ReturnType>& _Rhs) const + { + return (_M_Impl == _Rhs._M_Impl); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task<_ReturnType>& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _ASSERTE(_Ct != nullptr); + _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); + if (_Ct != details::_CancellationTokenState::_None()) + { + _M_Impl->_RegisterCancellation(_M_Impl); + } + } + + /// + /// Return the underlying implementation for this task. + /// + const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const + { + return _M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementation. + /// + void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = _Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementation using a move instead of a copy. + /// + void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) + { + _ASSERTE(!_M_Impl); + _M_Impl = std::move(_Impl); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _GetImpl()->_SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _GetImpl()->_SetTaskCreationCallstack(_callstack); + } + + /// + /// An internal version of then that takes additional flags and always execute the continuation inline by default. + /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. + /// This function is Used for runtime internal continuations only. + /// + template + auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, PPLX_CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template friend class task; + + + // The task handle type used to construct an 'initial task' - a task with no dependents. + template + struct _InitialTaskHandle : + details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t> + { + _Function _M_function; + _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _func) + : details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl) + , _M_function(_func) + { + } + + virtual ~_InitialTaskHandle() {} + + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func) const -> decltype(_func()) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + return _func(); + } + + void _Perform() const + { + _Init(_TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + this->_M_pTask->_Cancel(true); + } + + // + // Overload 0: returns _InternalReturnType + // + // This is the most basic task with no unwrapping + // + void _Init(details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function))); + } + + // + // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only under /ZW) + // or + // returns task<_InternalReturnType> + // + // This is task whose functor returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Init(details::_TypeSelectorAsyncOperationOrTask) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function)); + } + +#if defined (__cplusplus_winrt) + // + // Overload 2: returns IAsyncAction^ + // + // This is task whose functor returns an async action which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncAction) const + { + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ + // + // This is task whose functor returns an async operation with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } + + // + // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ + // + // This is task whose functor returns an async action with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_LogWorkItemAndInvokeUserLambda(_M_function))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + + /// + /// The task handle type used to create a 'continuation task'. + /// + template + struct _ContinuationTaskHandle : + details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + { + typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; + + typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; + typename details::_CopyableFunctor::type >::_Type _M_function; + + template + _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, + const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, + _ForwardedFunction&& _Func, const task_continuation_context & _Context, details::_TaskInliningMode_t _InliningMode) + : details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + ::_PPLTaskHandle(_ContinuationImpl) + , _M_ancestorTaskImpl(_AncestorImpl) + , _M_function(std::forward<_ForwardedFunction>(_Func)) + { + this->_M_isTaskBasedContinuation = _IsTaskBased::value; + this->_M_continuationContext = _Context; + this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); + this->_M_inliningMode = _InliningMode; + } + + virtual ~_ContinuationTaskHandle() {} + + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value) const -> decltype(_func(std::forward<_Arg>(_value))) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + CASABLANCA_UNREFERENCED_PARAMETER(_LogWorkItem); + return _func(std::forward<_Arg>(_value)); + } + + void _Perform() const + { + _Continue(_IsTaskBased(), _TypeSelection()); + } + + void _SyncCancelAndPropagateException() const + { + if (_M_ancestorTaskImpl->_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + this->_M_pTask->_Cancel(true); + } + } + + // + // Overload 0-0: _InternalReturnType -> _TaskType + // + // This is a straight task continuation which simply invokes its target with the ancestor's completion argument + // + void _Continue(std::false_type, details::_TypeSelectorNoAsync) const + { + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult())); + } + + // + // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only under /ZW) + // or + // _InternalReturnType -> task<_TaskType> + // + // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const + { + typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()) + ); + } + +#if defined (__cplusplus_winrt) + // + // Overload 0-2: _InternalReturnType -> IAsyncAction^ + // + // This is a straight task continuation which returns an async action which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()))); + } + + // + // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>(_OpWithProgress)); + } + + // + // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult()); + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); + } + +#endif /* defined (__cplusplus_winrt) */ + + // + // Overload 1-0: task<_InternalReturnType> -> _TaskType + // + // This is an exception handling type of continuation which takes the task rather than the task's result. + // + void _Continue(std::true_type, details::_TypeSelectorNoAsync) const + { + typedef task<_InternalReturnType> _FuncInputType; + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + this->_M_pTask->_FinalizeAndRunContinuations( + _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask))); + } + + // + // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ + // or + // task<_TaskType> + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation or a task which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))); + } + +#if defined (__cplusplus_winrt) + + // + // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async action which will be unwrapped for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } + + // + // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^ + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(this->_M_pTask, + ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>( + _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)))); + } +#endif /* defined (__cplusplus_winrt) */ + }; + + /// + /// Initializes a task using a lambda, function pointer or function object. + /// + template + void _TaskInitWithFunctor(const _Function& _Func) + { + typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; + + _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; + _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _M_Impl->_M_taskEventLogger._LogScheduleTask(false); + _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), details::_NoInline); + } + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) + { + _Event._RegisterTask(_M_Impl); + } + +#if defined (__cplusplus_winrt) + /// + /// Initializes a task using an asynchronous operation IAsyncOperation^ + /// + void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + _M_Impl->_M_fFromAsync = true; + + // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit + // returns a completion could execute concurrently and the task must be fully initialized before that happens. + _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; + // Pass the shared pointer into _AsyncInit for storage in the Async Callback. + details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp); + } + + /// + /// Initializes a task using an asynchronous operation IAsyncOperation^ + /// + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation::_Value>^ _AsyncOp) + { + _TaskInitAsyncOp(_AsyncOp); + } + + /// + /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress^ + /// + template + void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress::_Value, _Progress>^ _AsyncOp) + { + _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter::_Value, _Progress>(_AsyncOp)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _TaskInitWithFunctor<_ReturnType, _Function>(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + template + auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); + auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); + return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func), _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); + } + + /// + /// The one and only implementation of then for void and non-void tasks. + /// + template + auto _ThenImpl(_Function&& _Func, details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, + details::_TaskInliningMode_t _InliningMode = details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw invalid_operation("then() cannot be called on a default constructed task."); + } + + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; + typedef details::_TaskTypeTraits _Async_type_traits; + typedef typename _Async_type_traits::_TaskRetType _TaskType; + + // + // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a + // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user + // explicitly passes the same token. + // + if (_PTokenState == nullptr) + { + if (_Function_type_traits::_Takes_task::value) + { + _PTokenState = details::_CancellationTokenState::_None(); + } + else + { + _PTokenState = _GetImpl()->_M_pTokenState; + } + } + + task<_TaskType> _ContinuationTask; + _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); + + _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); + _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; + _ContinuationTask._SetTaskCreationCallstack(_CreationStack); + + _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( + _GetImpl(), _ContinuationTask._GetImpl(), std::forward<_Function>(_Func), _ContinuationContext, _InliningMode)); + + return _ContinuationTask; + } + + // The underlying implementation for this task + typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; +}; + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// For more information, see . +/// +/**/ +template<> +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef void result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_unitTask() + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) + { + details::_ValidateTaskConstructorArgs(_Param); + + _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); + // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the the call site of the task constructor. + _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : PPLX_CAPTURE_CALLSTACK()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other): _M_unitTask(_Other._M_unitTask){} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lambda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lambda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_unitTask = _Other._M_unitTask; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_unitTask = std::move(_Other._M_unitTask); + } + return *this; + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a Windows Store + /// style app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result + auto then(_Function&& _Func, cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _TaskOptions); + } + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + return _M_unitTask.wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + void get() const + { + _M_unitTask.get(); + } + + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + return _M_unitTask.is_done(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + scheduler_ptr scheduler() const + { + return _M_unitTask.scheduler(); + } + + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + return _M_unitTask.is_apartment_aware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task& _Rhs) const + { + return (_M_unitTask == _Rhs._M_unitTask); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// + void _CreateImpl(details::_CancellationTokenState * _Ct, scheduler_ptr _Scheduler) + { + _M_unitTask._CreateImpl(_Ct, _Scheduler); + } + + /// + /// Return the underlying implementation for this task. + /// + const details::_Task_ptr::_Type & _GetImpl() const + { + return _M_unitTask._M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementation. + /// + void _SetImpl(const details::_Task_ptr::_Type & _Impl) + { + _M_unitTask._SetImpl(_Impl); + } + + /// + /// Set the implementation of the task to be the supplied implementation using a move instead of a copy. + /// + void _SetImpl(details::_Task_ptr::_Type && _Impl) + { + _M_unitTask._SetImpl(std::move(_Impl)); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _M_unitTask._SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then method. + /// + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _M_unitTask._SetTaskCreationCallstack(_callstack); + } + + /// + /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. + /// + template + auto _Then(_Function&& _Func, details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _M_unitTask._ThenImpl(std::forward<_Function>(_Func), _PTokenState, task_continuation_context::use_default(), _Scheduler, PPLX_CAPTURE_CALLSTACK(), _InliningMode); + } + +private: + template friend class task; + template friend class task_completion_event; + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event& _Event) + { + _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); + } + +#if defined (__cplusplus_winrt) + /// + /// Initializes a task using an asynchronous action IAsyncAction^ + /// + void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); + } + + /// + /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^ + /// + template + void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) + { + _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); + } +#endif /* defined (__cplusplus_winrt) */ + + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _M_unitTask._TaskInitWithFunctor(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_T & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. + task _M_unitTask; +}; + +namespace details +{ + /// + /// The following type traits are used for the create_task function. + /// + +#if defined (__cplusplus_winrt) + // Unwrap functions for asyncOperations + template + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty>^); + + void _GetUnwrappedType(Windows::Foundation::IAsyncAction^); + + template + _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>^); + + template + void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress>^); +#endif /* defined (__cplusplus_winrt) */ + + // Unwrap task + template + _Ty _GetUnwrappedType(task<_Ty>); + + // Unwrap all supported types + template + auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); + // fallback + template + _Ty _GetUnwrappedReturnType(_Ty, ...); + + /// + /// _GetTaskType functions will retrieve task type T in task[T](Arg), + /// for given constructor argument Arg and its property "callable". + /// It will automatically unwrap argument to get the final return type if necessary. + /// + + // Non-Callable + template + _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); + + // Non-Callable + template + auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); + + // Callable + template + auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0)); + + // Special callable returns void + void _GetTaskType(std::function, std::true_type); + struct _BadArgType{}; + + template + auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0))); + + template + _BadArgType _FilterValidTaskType(_Ty _Param, ...); + + template + struct _TaskTypeFromParam + { + typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type; + }; +} // namespace details + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +template +__declspec(noinline) +auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> +{ + static_assert(!std::is_same::_Type,details::_BadArgType>::value, +#if defined (__cplusplus_winrt) + "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" +#else /* defined (__cplusplus_winrt) */ + "incorrect argument for create_task; can be a callable object or a task_completion_event" +#endif /* defined (__cplusplus_winrt) */ + ); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK()); + task::_Type> _CreatedTask(_Param, _TaskOptions); + return _CreatedTask; +} + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +template +__declspec(noinline) +task<_ReturnType> create_task(const task<_ReturnType>& _Task) +{ + task<_ReturnType> _CreatedTask(_Task); + return _CreatedTask; +} + +#if defined (__cplusplus_winrt) +namespace details +{ + template + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) + { + return task<_T>(op); + } + + template + task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) + { + return task<_T>(op); + } + + inline task _To_task_helper(Windows::Foundation::IAsyncAction^ op) + { + return task(op); + } + + template + task _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) + { + return task(op); + } + + template + class _ProgressDispatcherBase + { + public: + + virtual ~_ProgressDispatcherBase() + { + } + + virtual void _Report(const _ProgressType& _Val) = 0; + }; + + template + class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> + { + public: + + virtual ~_ProgressDispatcher() + { + } + + _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) + { + } + + virtual void _Report(const _ProgressType& _Val) + { + _M_ptr->_FireProgress(_Val); + } + + private: + + _ClassPtrType _M_ptr; + }; + class _ProgressReporterCtorArgType{}; +} // namespace details + +/// +/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound +/// to a particular asynchronous action or operation. +/// +/// +/// The payload type of each progress notification reported through the progress reporter. +/// +/// +/// This type is only available to Windows Store apps. +/// +/// +/**/ +template +class progress_reporter +{ + typedef std::shared_ptr> _PtrType; + +public: + + /// + /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. + /// + /// + /// The payload to report through a progress notification. + /// + /**/ + void report(const _ProgressType& _Val) const + { + _M_dispatcher->_Report(_Val); + } + + template + static progress_reporter _CreateReporter(_ClassPtrType _Ptr) + { + progress_reporter _Reporter; + details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); + _Reporter._M_dispatcher = _PtrType(_PDispatcher); + return _Reporter; + } + progress_reporter() {} + +private: + progress_reporter(details::_ProgressReporterCtorArgType); + + _PtrType _M_dispatcher; +}; + +namespace details +{ + // + // maps internal definitions for AsyncStatus and defines states that are not client visible + // + enum _AsyncStatusInternal + { + _AsyncCreated = -1, // externally invisible + // client visible states (must match AsyncStatus exactly) + _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started, + _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed, + _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled, + _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error, + // non-client visible internal states + _AsyncCancelPending, + _AsyncClosed, + _AsyncUndefined + }; + + // + // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results + // (which are progressively consumable between Start state and before Close is called) + // + enum _AsyncResultType + { + SingleResult = 0x0001, + MultipleResults = 0x0002 + }; + + // *************************************************************************** + // Template type traits and helpers for async production APIs: + // + + struct _ZeroArgumentFunctor { }; + struct _OneArgumentFunctor { }; + struct _TwoArgumentFunctor { }; + + // **************************************** + // CLASS TYPES: + + // ******************** + // TWO ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const); + + // ******************** + // ONE ARGUMENT: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + // non-void arg: + template + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const); + + // ******************** + // ZERO ARGUMENT: + + // void arg: + template + void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template + void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const); + + // void arg: + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const); + + // **************************************** + // POINTER TYPES: + + // ******************** + // TWO ARGUMENTS: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + // ******************** + // ONE ARGUMENT: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); + + // ******************** + // ZERO ARGUMENT: + + template + void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); + + template + struct _FunctorArguments + { + static const size_t _Count = 0; + }; + + template<> + struct _FunctorArguments<_OneArgumentFunctor> + { + static const size_t _Count = 1; + }; + + template<> + struct _FunctorArguments<_TwoArgumentFunctor> + { + static const size_t _Count = 2; + }; + + template + struct _FunctorTypeTraits + { + typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; + typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; + typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; + }; + + template + struct _FunctorTypeTraits<_T *> + { + typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; + typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; + typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; + }; + + template + struct _ProgressTypeTraits + { + static const bool _TakesProgress = false; + typedef void _ProgressType; + }; + + template + struct _ProgressTypeTraits> + { + static const bool _TakesProgress = true; + typedef typename _T _ProgressType; + }; + + + template::_ArgumentCount> + struct _CAFunctorOptions + { + static const bool _TakesProgress = false; + static const bool _TakesToken = false; + typedef void _ProgressType; + }; + + template + struct _CAFunctorOptions<_T, 1> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; + static const bool _TakesToken = !_TakesProgress; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + template + struct _CAFunctorOptions<_T, 2> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = true; + static const bool _TakesToken = true; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + }; + + ref class _Zip + { + }; + + // *************************************************************************** + // Async Operation Task Generators + // + + // + // Functor returns an IAsyncInfo - result needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator + { + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + template + struct _SelectorTaskGenerator<_AsyncSelector, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(), _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Cts.get_token()), _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress), _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } + }; + + // + // Functor returns a result - it needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType> + { + +#pragma warning(push) +#pragma warning(disable: 4702) + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(); + }, _taskOptinos); + } +#pragma warning(pop) + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress); + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>( [=]() -> _ReturnType { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(); + }, _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress); + }, _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task( [=]() { + _Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } + }; + + // + // Functor returns a task - the task can directly be returned: + // + template + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType> + { + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template<> + struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void> + { + template + static task _GenerateTask_0(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(); + } + + template + static task _GenerateTask_1C(const _Function& _Func, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Cts.get_token()); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _Func(_Progress, _Cts.get_token()); + } + }; + + template + struct _TaskGenerator + { + }; + + template + struct _TaskGenerator<_Generator, false, false> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, true, false> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, false, true> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + template + struct _TaskGenerator<_Generator, true, true> + { + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + }; + + // *************************************************************************** + // Async Operation Attributes Classes + // + // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in + // a single container. An attribute class must define: + // + // Mandatory: + // ------------------------- + // + // _AsyncBaseType : The Windows Runtime interface which is being implemented. + // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. + // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. + // _ReturnType : The return type of the async construct (void for actions / non-void for operations) + // + // _TakesProgress : An indication as to whether or not + // + // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task + // + // Optional: + // ------------------------- + // + + template + struct _AsyncAttributes + { + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> + { + typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; + typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _ProgressType _ProgressType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> + { + typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; + typedef void _ReturnType; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template + static task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + }; + + template + struct _AsyncLambdaTypeTraits + { + typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; + typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; + typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; + + static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; + static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; + + typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; + typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; + }; + + // *************************************************************************** + // AsyncInfo (and completion) Layer: + // + + // + // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) + // + template < typename _Attributes, _AsyncResultType resultType = SingleResult > + ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType + { + internal: + + _AsyncInfoBase() : + _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), + _M_errorCode(S_OK), + _M_completeDelegate(nullptr), + _M_CompleteDelegateAssigned(0), + _M_CallbackMade(0) + { + _M_id = ::pplx::details::platform::GetNextAsyncId(); + } + + public: + virtual typename _Attributes::_ReturnType GetResults() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual property unsigned int Id + { + unsigned int get() + { + _CheckValidStateForAsyncInfoCall(); + + return _M_id; + } + + void set(unsigned int id) + { + _CheckValidStateForAsyncInfoCall(); + + if (id == 0) + { + throw ::Platform::Exception::CreateException(E_INVALIDARG); + } + else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + + _M_id = id; + } + } + + virtual property Windows::Foundation::AsyncStatus Status + { + Windows::Foundation::AsyncStatus get() + { + _CheckValidStateForAsyncInfoCall(); + + _AsyncStatusInternal _Current = _M_currentStatus; + + // + // Map our internal cancel pending to canceled. This way "pending canceled" looks to the outside as "canceled" but + // can still transition to "completed" if the operation completes without acknowledging the cancellation request + // + switch(_Current) + { + case _AsyncCancelPending: + _Current = _AsyncCanceled; + break; + case _AsyncCreated: + _Current = _AsyncStarted; + break; + default: + break; + } + + return static_cast(_Current); + } + } + + virtual property Windows::Foundation::HResult ErrorCode + { + Windows::Foundation::HResult get() + { + _CheckValidStateForAsyncInfoCall(); + + Windows::Foundation::HResult _Hr; + _Hr.Value = _M_errorCode; + return _Hr; + } + } + + virtual property typename _Attributes::_ProgressDelegateType^ Progress + { + typename typename _Attributes::_ProgressDelegateType^ get() + { + return _GetOnProgress(); + } + + void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + _PutOnProgress(_ProgressHandler); + } + } + + virtual void Cancel() + { + if (_TransitionToState(_AsyncCancelPending)) + { + _OnCancel(); + } + } + + virtual void Close() + { + if (_TransitionToState(_AsyncClosed)) + { + _OnClose(); + } + else + { + if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + } + + virtual property typename _Attributes::_CompletionDelegateType^ Completed + { + typename _Attributes::_CompletionDelegateType^ get() + { + _CheckValidStateForDelegateCall(); + return _M_completeDelegate; + } + + void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) + { + _CheckValidStateForDelegateCall(); + // this delegate property is "write once" + if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) + { + _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); + _M_completeDelegate = _CompleteHandler; + // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below + // as perceived from _FireCompletion on another thread. + MemoryBarrier(); + if (_IsTerminalState()) + { + _FireCompletion(); + } + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT); + } + } + } + + + protected private: + + // _Start - this is not externally visible since async operations "hot start" before returning to the caller + void _Start() + { + if (_TransitionToState(_AsyncStarted)) + { + _OnStart(); + } + else + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE); + } + } + + + void _FireCompletion() + { + _TryTransitionToCompleted(); + + // we guarantee that completion can only ever be fired once + if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) + { + _M_completeDelegateContext._CallInContext([=] { + _M_completeDelegate((_Attributes::_AsyncBaseType^)this, this->Status); + _M_completeDelegate = nullptr; + }); + } + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) + { + throw ::Platform::Exception::CreateException(E_UNEXPECTED); + } + + bool _TryTransitionToCompleted() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); + } + + bool _TryTransitionToCancelled() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); + } + + bool _TryTransitionToError(const HRESULT error) + { + _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); + return _TransitionToState(_AsyncStatusInternal::_AsyncError); + } + + // This method checks to see if the delegate properties can be + // modified in the current state and generates the appropriate + // error hr in the case of violation. + inline void _CheckValidStateForDelegateCall() + { + if (_M_currentStatus == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method checks to see if results can be collected in the + // current state and generates the appropriate error hr in + // the case of a violation. + inline void _CheckValidStateForResultsCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + + if (_Current == _AsyncError) + { + throw ::Platform::Exception::CreateException(_M_errorCode); + } +#pragma warning(push) +#pragma warning(disable: 4127) // Conditional expression is constant + // single result illegal before transition to Completed or Cancelled state + if (resultType == SingleResult) +#pragma warning(pop) + { + if (_Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + // multiple results can be called after Start has been called and before/after Completed + else if (_Current != _AsyncStarted && + _Current != _AsyncCancelPending && + _Current != _AsyncCanceled && + _Current != _AsyncCompleted) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + } + + // This method can be called by derived classes periodically to determine + // whether the asynchronous operation should continue processing or should + // be halted. + inline bool _ContinueAsyncOperation() + { + return (_M_currentStatus == _AsyncStarted); + } + + // These two methods are used to allow the async worker implementation do work on + // state transitions. No real "work" should be done in these methods. In other words + // they should not block for a long time on UI timescales. + virtual void _OnStart() = 0; + virtual void _OnClose() = 0; + virtual void _OnCancel() = 0; + + private: + + // This method is used to check if calls to the AsyncInfo properties + // (id, status, errorcode) are legal in the current state. It also + // generates the appropriate error hr to return in the case of an + // illegal call. + inline void _CheckValidStateForAsyncInfoCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + if (_Current == _AsyncClosed) + { + throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL); + } + else if (_Current == _AsyncCreated) + { + throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED); + } + + } + + inline bool _TransitionToState(const _AsyncStatusInternal _NewState) + { + _AsyncStatusInternal _Current = _M_currentStatus; + + // This enforces the valid state transitions of the asynchronous worker object + // state machine. + switch(_NewState) + { + case _AsyncStatusInternal::_AsyncStarted: + if (_Current != _AsyncCreated) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCompleted: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCancelPending: + if (_Current != _AsyncStarted) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCanceled: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncError: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncClosed: + if (!_IsTerminalState(_Current)) + { + return false; + } + break; + default: + return false; + break; + } + + // attempt the transition to the new state + // Note: if currentStatus_ == _Current, then there was no intervening write + // by the async work object and the swap succeeded. + _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( + _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), + _NewState, + static_cast(_Current))); + + // ICE returns the former state, if the returned state and the + // state we captured at the beginning of this method are the same, + // the swap succeeded. + return (_RetState == _Current); + } + + inline bool _IsTerminalState() + { + return _IsTerminalState(_M_currentStatus); + } + + inline bool _IsTerminalState(_AsyncStatusInternal status) + { + return (status == _AsyncError || + status == _AsyncCanceled || + status == _AsyncCompleted || + status == _AsyncClosed); + } + + private: + + _ContextCallback _M_completeDelegateContext; + typename _Attributes::_CompletionDelegateType^ volatile _M_completeDelegate; + _AsyncStatusInternal volatile _M_currentStatus; + HRESULT volatile _M_errorCode; + unsigned int _M_id; + long volatile _M_CompleteDelegateAssigned; + long volatile _M_CallbackMade; + }; + + // *************************************************************************** + // Progress Layer (optional): + // + + template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > + ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + }; + + template< typename _Attributes, _AsyncResultType _ResultType> + ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType> + { + internal: + + _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), + _M_progressDelegate(nullptr) + { + } + + virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override + { + _CheckValidStateForDelegateCall(); + return _M_progressDelegate; + } + + virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override + { + _CheckValidStateForDelegateCall(); + _M_progressDelegate = _ProgressHandler; + _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); + } + + void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) + { + if (_M_progressDelegate != nullptr) + { + _M_progressDelegateContext._CallInContext([=] { + _M_progressDelegate((_Attributes::_AsyncBaseType^)this, _ProgressValue); + }); + } + } + + private: + + _ContextCallback _M_progressDelegateContext; + typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; + }; + + template + ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> + { + }; + + // *************************************************************************** + // Task Adaptation Layer: + // + + // + // _AsyncTaskThunkBase provides a bridge between IAsync and task. + // + template + ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> + { + public: + + virtual _ReturnType GetResults() override + { + _CheckValidStateForResultsCall(); + return _M_task.get(); + } + + internal: + + typedef task<_ReturnType> _TaskType; + + _AsyncTaskThunkBase(const _TaskType& _Task) + : _M_task(_Task) + { + } + + _AsyncTaskThunkBase() + { + } + + protected: + + virtual void _OnStart() override + { + _M_task.then( [=](_TaskType _Antecedent) { + try + { + _Antecedent.get(); + } + catch(task_canceled&) + { + _TryTransitionToCancelled(); + } + catch(::Platform::Exception^ _Ex) + { + _TryTransitionToError(_Ex->HResult); + } + catch(...) + { + _TryTransitionToError(E_FAIL); + } + _FireCompletion(); + }); + } + + internal: + + _TaskType _M_task; + cancellation_token_source _M_cts; + }; + + template + ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> + { + internal: + + _AsyncTaskThunk(const _TaskType& _Task) : + _AsyncTaskThunkBase(_Task) + { + } + + _AsyncTaskThunk() + { + } + + protected: + + virtual void _OnClose() override + { + } + + virtual void _OnCancel() override + { + _M_cts.cancel(); + } + }; + + // *************************************************************************** + // Async Creation Layer: + // + template + ref class _AsyncTaskGeneratorThunk sealed : _AsyncTaskThunk::_AsyncAttributes> + { + internal: + + typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; + typedef typename _AsyncTaskThunk<_Attributes> _Base; + typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; + + _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) + { + // Virtual call here is safe as the class is declared 'sealed' + _Start(); + } + + protected: + + // + // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, + // let the base thunk handle everything. + // + + virtual void _OnStart() override + { + // + // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, + // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. + // + _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack); + _Base::_OnStart(); + } + + virtual void _OnCancel() override + { + _Base::_OnCancel(); + } + + private: + _TaskCreationCallstack _M_creationCallstack; + _Function _M_func; + }; +} // namespace details + +/// +/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is +/// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or +/// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. +/// +/// +/// The lambda or function object from which to create a Windows Runtime asynchronous construct. +/// +/// +/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an +/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. +/// +/// +/// The return type of the lambda determines whether the construct is an action or an operation. +/// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of +/// operations of TResult. +/// The lambda may also return a task<TResult> which encapsulates the asynchronous work within itself or is the continuation of +/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that +/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. +/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will +/// cause the creation of operations of TResult. +/// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and +/// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without +/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous +/// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that +/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the +/// asynchronous construct causes cancellation of those tasks. +/// If the body of the lambda or function object returns a result (and not a task<TResult>), the lambda will be executed +/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will +/// cause cancellation of the implicit task. +/// If the body of the lambda returns a task, the lambda executes inline, and by declaring the lambda to take an argument of type +/// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. +/// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on +/// the async operation or action produced.. +/// This function is only available to Windows Store apps. +/// +/// +/// +/// +/**/ +template +__declspec(noinline) +details::_AsyncTaskGeneratorThunk<_Function> ^create_async(const _Function& _Func) +{ + static_assert(std::is_same::value, + "argument to create_async must be a callable object taking zero, one or two arguments"); + return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, PPLX_CAPTURE_CALLSTACK()); +} + +#endif /* defined (__cplusplus_winrt) */ + +namespace details +{ + // Helper struct for when_all operators to know when tasks have completed + template + struct _RunAllParam + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + if (!_SkipVector) + { + _M_vector._Result.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + _ResultHolder > _M_vector; + _ResultHolder<_Type> _M_mergeVal; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + template + struct _RunAllParam > + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + + if (!_SkipVector) + { + _M_vector.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + std::vector<_ResultHolder > > _M_vector; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + // Helper struct specialization for void + template<> + struct _RunAllParam<_Unit_type> + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len) + { + _M_numTasks = _Len; + } + + task_completion_event<_Unit_type> _M_completed; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc, _CancellationTokenState *_PJoinedTokenState) + { + if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None()) + { + cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState); + _T.register_callback( [=](){ + _MergedSrc.cancel(); + }); + } + } + + template + void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) + { + if (_Task._GetImpl()->_IsCompleted()) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // Inline execute its direct continuation, the _ReturnTask + _PParam->_M_completed.set(_Unit_type()); + // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled()); + if (_Task._GetImpl()->_HasUserException()) + { + // _Cancel will return false if the TCE is already canceled with or without exception + _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); + } + else + { + _PParam->_M_completed._Cancel(); + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + } + + template + struct _WhenAllImpl + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_ElementType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + return _PParam->_M_vector.Get(); + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ + _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl, _Iterator> + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { + _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks); + std::vector<_ElementType> _Result; + for(size_t _I = 0; _I < _PParam->_M_numTasks; _I++) + { + const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); + _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task> _ResultTask) { + + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { + _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl + { + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + + auto _PParam = new _RunAllParam<_Unit_type>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) { + }, nullptr); + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + _JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if( _Begin == _End ) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam](task _ResultTask) { + auto _Func = [](){}; + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + } + } + + return _ReturnTask; + } + }; + + template + task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, + bool _OutputVectorFirst) + { + auto _PParam = new _RunAllParam<_ReturnType>(); + cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { + _ASSERTE(_PParam->_M_completeCount == 2); + auto _Result = _PParam->_M_vector.Get(); // copy by value + auto _mergeVal = _PParam->_M_mergeVal.Get(); + + if (_OutputVectorFirst == true) + { + _Result.push_back(_mergeVal); + } + else + { + _Result.insert(_Result.begin(), _mergeVal); + } + return _Result; + }, nullptr); + + // Step2: Combine and check tokens. + _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); + _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); + + // Step3: Check states of previous tasks. + _PParam->_Resize(2, true); + + if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + _VectorTask._Then([_PParam](task> _ResultTask) { + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_vector.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) { + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_mergeVal.Set(_ResultLocal); + }; + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + return _ReturnTask; + } +} // namespace details + +/// +/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes successfully when all of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encountered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encountered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) +{ + task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +/// +/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encountered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true)) +{ + return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); +} + +/// +/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encountered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false)) +{ + return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); +} + +/// +/// Creates a task that will complete successfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encountered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto operator&&(const task> & _Lhs, const task> & _Rhs) -> decltype(when_all(&_Lhs, &_Lhs)) +{ + task> _PTasks[2] = {_Lhs, _Rhs}; + return when_all(_PTasks, _PTasks+2); +} + +namespace details +{ + // Helper struct for when_any operators to know when tasks have completed + template + struct _RunAnyParam + { + _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false) + { + } + ~_RunAnyParam() + { + if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) + _M_exceptionRelatedToken->_Release(); + } + task_completion_event<_CompletionType> _M_Completed; + cancellation_token_source _M_cancellationSource; + _CancellationTokenState * _M_exceptionRelatedToken; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + bool _M_fHasExplicitToken; + }; + + template + void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) + { + bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); + if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) + { + _Func(); + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + delete _PParam; + } + } + else + { + _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); + if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) + { + if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) + { + // This can only enter once. + _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; + _ASSERTE(_PParam->_M_exceptionRelatedToken); + // Deref token will be done in the _PParam destructor. + if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None()) + { + _PParam->_M_exceptionRelatedToken->_Reference(); + } + } + } + + if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) + { + // If no one has be completed so far, we need to make some final cancellation decision. + if (!_PParam->_M_Completed._IsTriggered()) + { + // If we already explicit token, we can skip the token join part. + if (!_PParam->_M_fHasExplicitToken) + { + if (_PParam->_M_exceptionRelatedToken) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); + } + else + { + // If haven't captured any exception token yet, there was no exception for all those tasks, + // so just pick a random token (current one) for normal cancellation. + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); + } + } + // Do exception cancellation or normal cancellation based on whether it has stored exception. + _PParam->_M_Completed._Cancel(); + } + delete _PParam; + } + } + } + + template + struct _WhenAnyImpl + { + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam, _CancellationTokenState *>>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); + }; + + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair, _CancellationTokenState *> _Result) -> std::pair<_ElementType, size_t> { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; + + template + struct _WhenAnyImpl + { + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) + { + if( _Begin == _End ) + { + throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } + + _CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _PParam = new _RunAnyParam>(); + + if (_PTokenState) + { + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task> _Any_tasks_completed(_PParam->_M_Completed, _Options); + + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task _ResultTask) { + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = _Index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, _CancellationTokenState::_None()); + + _Index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair _Result) -> size_t { + _ASSERTE(_Result.second); + if (!_PTokenState) + { + _JoinAllTokens_Add(_CancellationSource, _Result.second); + } + return _Result.first; + }, nullptr); + } + }; +} // namespace details + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) + -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting +/// task will receive the cancellation token of the task that causes it to complete. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken) + -> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam, details::_CancellationTokenState *>>(); + + task, details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, details::_CancellationTokenState *> _Ret) -> std::vector<_ReturnType> { + _ASSERTE(_Ret.second); + _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + return _Ret.first; + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + _Lhs._Then([_PParam](task> _ResultTask) { + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + + _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) + { + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + + std::vector<_ReturnType> _Vec; + _Vec.push_back(_Result); + _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +auto operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) -> decltype(_Rhs || _Lhs) +{ + return _Rhs || _Lhs; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template, typename _Pair = std::pair> +_Ty operator||(const task & _Lhs_arg, const task & _Rhs_arg) +{ + const _Ty& _Lhs = _Lhs_arg; + const _Ty& _Rhs = _Rhs_arg; + auto _PParam = new details::_RunAnyParam<_Pair>(); + + task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_task_completed._Then([=](_Pair _Ret) { + _ASSERTE(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + }, nullptr); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](_Ty _ResultTask) mutable { + // Dev10 compiler needs this. + auto _PParam1 = _PParam; + auto _Func = [&_ResultTask, _PParam1]() { + _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + }; + + _Lhs._Then(_Continuation, details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, details::_CancellationTokenState::_None()); + + return _ReturnTask; +} + +template +task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _Tce; + _Tce.set(_Param); + return create_task(_Tce, _TaskOptions); +} + +template +inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _Tce; + _Tce.set(); + return create_task(_Tce, _TaskOptions); +} + +template +task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_TaskType> _Tce; + _Tce.set_exception(_Exception); + return create_task(_Tce, _TaskOptions); +} + +} // namespace Concurrency + +#pragma pop_macro("new") + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#pragma pack(pop) + +#endif // (defined(_MSC_VER) && (_MSC_VER >= 1800)) + +#ifndef _CONCRT_H +#ifndef _LWRCASE_CNCRRNCY +#define _LWRCASE_CNCRRNCY +// Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace +// is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist. +namespace Concurrency {} +namespace concurrency = Concurrency; +#endif +#endif + +#endif // PPLXTASKS_H