/**** ==++==** Copyright (c) Microsoft Corporation. All rights reserved.** ==--==* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+** ppl_tasks.h** Parallel Patterns Library - First Class Tasks** =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-****/ #pragma once #ifndef _PPLTASKS_H#define _PPLTASKS_H #include <ppl.h>#include <vector>#include <utility> #if defined(__cplusplus_winrt)#include <windows.h>#if 0 // ASYNC TODO: fix this when ctxtcall.h becomes part of VS Express (and remove the entry from msvc40.if)#include <ctxtcall.h>#else#ifndef __ctxtcall_h__#define __ctxtcall_h__ /* Forward Declarations */ #ifndef __IContextCallback_FWD_DEFINED__#define __IContextCallback_FWD_DEFINED__typedef interface IContextCallback IContextCallback;#endif /* __IContextCallback_FWD_DEFINED__ */ /* interface __MIDL_itf_ctxtcall_0000_0000 *//* [local] */ //+-----------------------------------------------------------------//// Microsoft Windows// Copyright (c) Microsoft Corporation. All rights reserved.////------------------------------------------------------------------typedef struct tagComCallData { DWORD dwDispid; DWORD dwReserved;
void *pUserDefined; } ComCallData; extern RPC_IF_HANDLE __MIDL_itf_ctxtcall_0000_0000_v0_0_c_ifspec;extern RPC_IF_HANDLE __MIDL_itf_ctxtcall_0000_0000_v0_0_s_ifspec; #ifndef __IContextCallback_INTERFACE_DEFINED__#define __IContextCallback_INTERFACE_DEFINED__ /* interface IContextCallback *//* [unique][uuid][object][local] */ typedef /* [ref] */ HRESULT ( __stdcall *PFNCONTEXTCALL )( ComCallData *pParam); EXTERN_C const IID IID_IContextCallback; MIDL_INTERFACE("000001da-0000-0000-C000-000000000046")IContextCallback : public IUnknown{public: virtual HRESULT STDMETHODCALLTYPE ContextCallback( /* [in] */ PFNCONTEXTCALL pfnCallback, /* [in] */ ComCallData *pParam, /* [in] */ REFIID riid, /* [in] */ int iMethod, /* [in] */ IUnknown *pUnk) = 0; }; #endif /* __IContextCallback_INTERFACE_DEFINED__ */#endif // __ctxtcall_h__#endif // #if 0 (#include <ctxtcall.h>) // TODO: Remove this once we have all the right headers sitting in WCConcRT and the Rascal builds we are using to build /ZW applications// with PPLTASKS. #ifndef E_ILLEGAL_METHOD_CALL#define E_ILLEGAL_METHOD_CALL _HRESULT_TYPEDEF_(0x8000000EL)#endif #ifndef E_ILLEGAL_STATE_CHANGE#define E_ILLEGAL_STATE_CHANGE _HRESULT_TYPEDEF_(0x8000000DL)#endif #endif #ifdef _DEBUG #define _DBG_ONLY(X) X#else #define _DBG_ONLY(X)#endif #pragma pack(push,_CRT_PACKING)
#pragma warning(push)#pragma warning(disable: 28197) /// <summary>/// The <c>Concurrency</c> namespace provides classes and functions that give you access to the Concurrency Runtime,/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>./// </summary>/**/namespace Concurrency{namespace preview{// The task_status enum simply mirrors the task_group_status enumtypedef task_group_status task_status; template <typename _Type> class task;template <> class task<void>;class task_continuation_context; /// <summary>/// This class describes an exception that is thrown by the PPL tasks layer in order to force the current task/// to cancel. It is also thrown by the <c>get()</c> method on <see cref="task Class">task</see>, if the task/// is canceled when the method is invoked./// </summary>/**/class task_canceled : public std::exception{public: /// <summary> /// Constructs a <c>task_canceled</c> object. /// </summary> /// <param name="_Message"> /// A descriptive message of the error. /// </param> /**/ explicit task_canceled(const char * _Message) throw() : exception(_Message) { } /// <summary> /// Constructs a <c>task_canceled</c> object. /// </summary> /**/ task_canceled() throw() : exception() { }};
namespace details{ // // PREVIEW: Dummy definition to get the header to compile // class _Task_canceled : public std::exception { public: explicit _CRTIMP _Task_canceled(const char * _Message) throw() : exception(_Message) { } _CRTIMP _Task_canceled() throw() : exception() { } }; typedef unsigned char _Unit_type; struct _TypeSelectorNoAsync {}; struct _TypeSelectorAsyncOperationOrTask {}; struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; struct _TypeSelectorAsyncAction {}; struct _TypeSelectorAsyncActionWithProgress {}; struct _TypeSelectorAsyncOperationWithProgress {}; template<typename _Ty> struct _NormalizeVoidToUnitType { typedef _Ty _Type; }; template<> struct _NormalizeVoidToUnitType<void> { typedef _Unit_type _Type; }; template<typename _T> struct _IsUnwrappedAsyncSelector { static const bool _Value = true; }; template<> struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> { static const bool _Value = false; }; template <typename _Ty>
struct _UnwrapTaskType { typedef _Ty _Type; }; template <typename _Ty> struct _UnwrapTaskType<::Concurrency::preview::task<_Ty>> { typedef _Ty _Type; }; template <typename _T> _TypeSelectorAsyncTask _AsyncOperationKindSelector(::Concurrency::preview::task<_T>); _TypeSelectorNoAsync _AsyncOperationKindSelector(...); template <typename _Type> struct _FixHat { typedef _Type _FixedType; }; #if defined(__cplusplus_winrt) template <typename _Type> struct _Unhat { typedef _Type _Value; }; template <typename _Type> struct _Unhat<_Type^> { typedef _Type _Value; }; template <typename _Type> struct _FixHat<_Type^> { typedef _Type^ const & _FixedType; }; value struct _NonUserType {}; template <typename _Type, bool _IsValueTypeOrRefType = __is_value_class(_Type)> struct _ValueTypeOrRefType { typedef _NonUserType _Value; }; template <typename _Type> struct _ValueTypeOrRefType<_Type^, false> { typedef typename _Type^ _Value;
}; template <typename _Type> struct _ValueTypeOrRefType<_Type, true> { typedef _Type _Value; }; template <typename _T1, typename _T2> _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1,_T2>^); template <typename _T1> _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1>^); template <typename _Type> struct _GetProgressType { typedef decltype(_ProgressTypeSelector(std::declval<_Type>())) _Value; }; template <typename _Type> struct _IsIAsyncInfo { static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value); }; template <typename _T> _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T>^); _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction^); template <typename _T1, typename _T2> _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>^); template <typename _T> _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T>^); template <typename _Type, bool _IsAsync = _IsIAsyncInfo<_Type>::_Value> struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(std::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = _IsAsync;
static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; template<typename _Type> 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 <typename _Type> struct _IsIAsyncInfo { static const bool _Value = false; }; template <typename _Type, bool _IsAsync = false> struct _TaskTypeTraits { typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType; typedef decltype(_AsyncOperationKindSelector(std::declval<_Type>())) _AsyncKind; typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value; }; #endif #pragma warning(push)#pragma warning(disable: 4100) template <typename _Function> auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type()) { return std::true_type(); }#pragma warning(pop) template <typename _Function> std::false_type _IsCallable(_Function, ...) { return std::false_type(); } template <typename _Type> struct _FixFunction; // ASYNC TODO: this is a work-around for compiler bug 246576. It must removed in Beta. template <typename _Ret, typename _Arg> struct _FixFunction< std::function<_Ret (_Arg)>> {
typedef typename _FixHat<_Arg>::_FixedType _FixedArg; typedef std::function<_Ret (_FixedArg)> _FixedFunc; }; template <> struct _TaskTypeTraits<void> { typedef void _TaskRetType; typedef _TypeSelectorNoAsync _AsyncKind; typedef _Unit_type _NormalizedTaskRetType; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; template<typename _Type> task<_Type> _To_task(_Type t); task<void> _To_task(); template <typename _Function, typename _Type> auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(std::declval<_Type>()))); template <typename _Function, typename _Type> auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(std::declval<_Type>())); template <typename _Function, typename _Type> auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(std::declval<_Type>())), std::true_type()); template <typename _Function, typename _Type> std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...); template <typename _Function> auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task())); template <typename _Function> auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func()); template <typename _Function> auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task()), std::true_type()); template <typename _Function> std::false_type _VoidIsTaskHelper(_Function _Func, int, ...); template<typename _Function, typename _Type> struct _FunctionTypeTraits { typedef decltype(_ReturnTypeHelper(std::declval<_Type>(),std::declval<_Function>(), 0, 0)) _FuncRetType; typedef decltype(_IsTaskHelper(std::declval<_Type>(),std::declval<_Function>(), 0, 0)) _Takes_task; }; template<typename _Function> struct _FunctionTypeTraits<_Function, void>
{ typedef decltype(_VoidReturnTypeHelper(std::declval<_Function>(), 0, 0)) _FuncRetType; typedef decltype(_VoidIsTaskHelper(std::declval<_Function>(), 0, 0)) _Takes_task; }; template<typename _Function, typename _ReturnType> struct _ContinuationTypeTraits { typedef typename task<typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_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<task<void>> t1([]() -> task<void> { // task<void> t2([]() {}); // return t2; // }); // // This declaration SHOULD cause unwrapping // task<void>> t1([]() -> task<void> { // task<void> 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 <typename _TaskType, typename _FuncRetType> struct _InitFunctorTypeTraits { typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; }; template<typename T> struct _InitFunctorTypeTraits<T, T> { typedef _TypeSelectorNoAsync _AsyncKind; static const bool _IsAsyncTask = false; static const bool _IsUnwrappedTaskOrAsync = false; }; template<typename _Function> struct _TaskProcThunk { _TaskProcThunk(const _Function& _Callback) : _Func(_Callback)
{ } static void _Bridge(void *_PData) { _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); _PThunk->_Func(); delete _PThunk; } private: _Function _Func; _TaskProcThunk& operator=(const _TaskProcThunk&); }; template<typename _Function> void _ScheduleLightweightTask(_Function _Func) { typedef _TaskProcThunk<_Function> _CallbackType; _CallbackType *_PThunk = new _CallbackType(_Func); ::Concurrency::details::_CurrentScheduler::_ScheduleTask(reinterpret_cast<TaskProc>(&_CallbackType::_Bridge), _PThunk); } // ASYNC TODO: This should be based on a WinRT object which is not free threaded and an agile<> reference. With such, it should be a few lines // of code. As I understand the current state of things, unfortunately neither of these exist at the moment; hence -- this is relying // on RAW COM. As well, doing this requires an APPX manifest. Can we enforce this in headers given how things are being compiled!? // // Even if not all of this can be replaced, _ResultContext stuff can likely be replaced with Agile<>. class _ContextCallback { typedef std::function<void(void)> _CallbackFunction; #if defined(__cplusplus_winrt) public: static _ContextCallback _CaptureCurrent() { _ContextCallback _Context; _Context._Capture(); return _Context; } ~_ContextCallback() { _Reset();
} _ContextCallback(bool _DeferCapture = false) { if (_DeferCapture) { _Context._CaptureMethod = _CaptureDeferred; } else { _Context._M_pContextCallback = nullptr; } } // Resolves a context that was created as _CaptureDeferred based on the environment (ancestor, current context). void _Resolve(bool _CaptureCurrent) { if(_Context._CaptureMethod == _CaptureDeferred) { if (_CaptureCurrent && _IsCurrentOriginSTA()) { _Capture(); } else { _Context._M_pContextCallback = nullptr; } } } void _Capture() { HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast<void **>(&_Context._M_pContextCallback)); if (!SUCCEEDED(_Hr)) { _Context._M_pContextCallback = nullptr; } } _ContextCallback(const _ContextCallback& _Src) { _Assign(_Src._Context._M_pContextCallback); } _ContextCallback(_ContextCallback&& _Src) { _Context._M_pContextCallback = _Src._Context._M_pContextCallback; _Src._Context._M_pContextCallback = nullptr; } _ContextCallback& operator=(const _ContextCallback& _Src) { if (this != &_Src)
{ _Reset(); _Assign(_Src._Context._M_pContextCallback); } return *this; } _ContextCallback& operator=(_ContextCallback&& _Src) { if (this != &_Src) { _Context._M_pContextCallback = _Src._Context._M_pContextCallback; _Src._Context._M_pContextCallback = nullptr; } return *this; } bool _HasCapturedContext() const { _ASSERT(_Context._CaptureMethod != _CaptureDeferred); return (_Context._M_pContextCallback != nullptr); } void _CallInContext(_CallbackFunction _Func) const { if (!_HasCapturedContext()) { _Func(); } else { ComCallData callData; memset(&callData, 0, sizeof(callData)); callData.pUserDefined = reinterpret_cast<void *>(&_Func); HRESULT _Hr = _Context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_IContextCallback, 5, nullptr); if (!SUCCEEDED(_Hr)) {#pragma warning(push)#pragma warning(disable: 4673 4672) // ASYNC TODO: remove the pragma when the fix for TFS#209378 comes to our branch throw ref new Platform::COMException(_Hr);#pragma warning(pop) } } } bool operator==(const _ContextCallback& _Rhs) const { return (_Context._M_pContextCallback == _Rhs._Context._M_pContextCallback); }
bool operator!=(const _ContextCallback& _Rhs) const { return !(operator==(_Rhs)); } private: void _Reset() { if (_Context._CaptureMethod != _CaptureDeferred && _Context._M_pContextCallback != nullptr) { _Context._M_pContextCallback->Release(); } } void _Assign(IContextCallback *_PContextCallback) { _Context._M_pContextCallback = _PContextCallback; if (_Context._CaptureMethod != _CaptureDeferred && _Context._M_pContextCallback != nullptr) { _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 / WinRT apartment as far as task continuations need know) 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 aparment 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 _CaptureMethod; } _Context; static const size_t _CaptureDeferred = 1;#else 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<typename _Type> struct _ResultContext { static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) { return _ContextCallback(); } static _Type _GetValue(_Type _ObjInCtx, const _ContextCallback & /* _Ctx */, bool /* _RuntimeAggregate */) { return _ObjInCtx; } }; #if defined(__cplusplus_winrt) template<typename _Type> struct _MarshalHelper { static _Type^ _Perform(_Type^ _ObjInCtx, const _ContextCallback& _Ctx) { HRESULT _Hr; LPSTREAM _PStream; _Ctx._CallInContext([&](){ // It isn't safe to simply reinterpret_cast a hat type to IUnknown* because some hat type do not have a real vtable ptr // Instead, we need to first upcast it to Object^ to make it "grow" the vtable ptr Platform::Object^ _PObj = safe_cast<Platform::Object^>(_ObjInCtx); IUnknown* _PUnk = reinterpret_cast<IUnknown*>(_PObj); _Hr = CoMarshalInterThreadInterfaceInStream(__uuidof(_Type^), _PUnk, &_PStream); }); // // ASYNC TODO: Is this right? Right now without an APPX manifest
, classes you define in your C++ WinRT code are not registered meaning this // *WILL* fail! // if (!SUCCEEDED(_Hr)) { return _ObjInCtx; } _Type^ _Proxy; _Hr = CoGetInterfaceAndReleaseStream(_PStream, __uuidof(_Type^), reinterpret_cast<void**>(&_Proxy)); if (!SUCCEEDED(_Hr)) { throw ref new Platform::COMException(_Hr); } return _Proxy; } }; // Specializations to avoid marshaling for strings and arrays. ASYNC TODO: rewrite using a special compiler type trait template<typename _Type> struct _MarshalHelper<lang::array<_Type^>> { static lang::array<_Type^>^ _Perform(lang::array<_Type^>^ _ObjInCtx, const _ContextCallback& _Ctx) { return _ObjInCtx; } }; template<> struct _MarshalHelper<Platform::String> { static Platform::String^ _Perform(Platform::String^ _ObjInCtx, const _ContextCallback& _Ctx) { return _ObjInCtx; } }; template<typename _Type> _Type^ _Marshal(_Type^ _ObjInCtx, const _ContextCallback& _Ctx) { return _MarshalHelper<_Type>::_Perform(_ObjInCtx, _Ctx); } template<typename _Type> struct _InContext { static _Type _Get(_Type _ObjInCtx, const _ContextCallback& _Ctx) { return _ObjInCtx; } };
template<typename _Type> struct _InContext<_Type^> { static _Type^ _Get(_Type^ _ObjInCtx, const _ContextCallback& _Ctx) { _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) { return _ObjInCtx; } // // The object is from another apartment. If it's marshalable, do so. // return _Marshal<_Type>(_ObjInCtx, _Ctx); } }; template<typename _Type> struct _ResultContext<_Type^> { static _Type^ _GetValue(_Type^ _ObjInCtx, const _ContextCallback& _Ctx, bool /* _RuntimeAggregate */) { return _InContext<_Type^>::_Get(_ObjInCtx, _Ctx); } static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) { return _ContextCallback::_CaptureCurrent(); } }; // // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. // template<typename _Type> struct _ResultContext<std::vector<_Type^>> { static std::vector<_Type^> _GetValue(std::vector<_Type^> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) { if (!_RuntimeAggregate) { return _ObjInCtx; } _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext)
{ return _ObjInCtx; } for (auto _It = _ObjInCtx.begin(); _It != _ObjInCtx.end(); ++_It) { *_It = _Marshal<_Type>(*_It, _Ctx); } return _ObjInCtx; } static _ContextCallback _GetContext(bool _RuntimeAggregate) { if (!_RuntimeAggregate) { return _ContextCallback(); } else { return _ContextCallback::_CaptureCurrent(); } } }; template<typename _Type> struct _ResultContext<std::pair<_Type^, size_t>> { static std::pair<_Type^, size_t> _GetValue(std::pair<_Type^, size_t> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) { if (!_RuntimeAggregate) { return _ObjInCtx; } _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) { return _ObjInCtx; } return std::pair<_Type^, size_t>(_Marshal<_Type>(_ObjInCtx.first, _Ctx), _ObjInCtx.second); } static _ContextCallback _GetContext(bool _RuntimeAggregate) { if (!_RuntimeAggregate) { return _ContextCallback(); } else {
return _ContextCallback::_CaptureCurrent(); } } }; #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() or schedules a task-based continuation 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, // an unobserved_task_exception is thrown by the runtime. struct _ExceptionHolder { explicit _ExceptionHolder(const std::exception_ptr& _E) : _M_exceptionObserved(0), _M_storedException(_E) { } ~_ExceptionHolder() { if (_M_exceptionObserved == 0) { // Throwing an exception from a destructor will terminate the process if the destructor is executing as part of stack // unwinding due to an existing exception. In general, this should be fine here, since we're throwing this exception in // order to terminate the process.// PREVIEW_DISABLED// throw unobserved_task_exception(_M_storedException); } } void _SetExceptionObserved() { if (_M_exceptionObserved == 0) { _InterlockedExchange(&_M_exceptionObserved, 1); } } long volatile _M_exceptionObserved; std::exception_ptr _M_storedException; }; struct _Task_impl_base; template<typename _ReturnType> struct _Task_impl; template<typename _ReturnType> struct _Task_ptr { typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type;
static _Type make() { return std::make_shared<_Task_impl<_ReturnType>>(); } }; /// <summary> /// The _PPLTaskHandle is the individual work item that the PPL Task is expected to execute. The user function /// executed by the PPL task must be wrapped in one of these handles in order to execute properly on the underlying /// task collection. The lifetime of the task collection is owned by the task impl. /// </summary> /// <typeparam name="_Function"> /// The type of the function object that will be invoked to execute the body of the PPL task handle. /// </typeparam> /**/ template<typename _Function> class _PPLTaskHandle : public ::Concurrency::details::_PPLTaskChore { public: _PPLTaskHandle(const _Function& _Func, _Task_impl_base * _Task_impl) : _M_function(_Func), _M_pTask(_Task_impl) { m_pFunction = reinterpret_cast <TaskProc> (&::Concurrency::details::_UnrealizedChore::_InvokeBridge<_PPLTaskHandle>); } ~_PPLTaskHandle() { } void operator()() const { // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled // by the runtime. _ASSERT(_M_pTask != NULL); try { _M_function(); } catch (const task_canceled &) { _M_pTask->_Cancel(true, 0); throw; } catch (const _Task_canceled &) { _M_pTask->_Cancel(true, 0); throw; } catch (...) {
_M_pTask->_CancelWithException(std::current_exception(), 0); throw; } } private: // The task that is executing this function. _Task_impl_base * _M_pTask; // The function object invoked to perform the body of the task. _Function _M_function; _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator }; struct _TaskContinuationParameterBase; typedef void (__cdecl * _TaskContinuationProc)(_TaskContinuationParameterBase *, size_t); struct _TaskProcWithParam { _TaskContinuationProc _M_proc; _TaskContinuationParameterBase* _M_param; _TaskProcWithParam(_TaskContinuationProc _P_proc, _TaskContinuationParameterBase* _P_param) : _M_proc(_P_proc), _M_param(_P_param) {} }; /// <summary> /// The base implementation of a first-class task. This class contains all the non-type specific /// implementation details of the task. /// </summary> /**/ struct _Task_impl_base {private: // Must not be copied by value: _Task_impl_base(const _Task_impl_base&); _Task_impl_base const & operator=(_Task_impl_base const&);public: enum _TaskInternalState { // Tracks the state of the task, rather than the task collection on which the task is scheduled _Created, _Started, _PendingCancel, _Completed, _Canceled }; _Task_impl_base() : _M_TaskState(_Created), _M_pTaskCollection(NULL), _M_fObservesExceptions(false), _M_pExecutingChore(NULL), _M_fFromAsync(false), _M_fRuntimeAggregate
(false), _M_fUnwrappedTask(false) { } virtual ~_Task_impl_base() { // This destructor is called when the last reference to the _Task_impl_base goes away. // References are held by the task<T> class as well as the _PPLTaskHandle executing on the // TaskCollection. delete _M_pTaskCollection; } task_status _Wait(bool _FromWait) {#if defined(__cplusplus_winrt) // In order to prevent WinRT STA threads from blocking the UI, calling task.wait() is illegal for STA // threads, and task.get() is illegal if it has to wait. if (_IsSTA() && (_FromWait || _M_Completed.wait(0) == COOPERATIVE_WAIT_TIMEOUT)) { throw invalid_operation("Illegal to wait on a task in a WinRT STA"); }#endif // defined(__cplusplus_winrt) // Wait for the task to be actually scheduled, otherwise the underlying task collection // might not be created yet. If we don't wait, we will miss the chance to inline this task. _M_Scheduled.wait(); // A PPL task created by a task_completion_event does not have an underlying TaskCollection. For // These tasks, a call to wait should wait for the event to be set. The TaskCollection must either // be NULL or allocated (the setting of _M_Scheduled) ensures that. // If this task was created from a WinRT 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_pTaskCollection == NULL || _M_fFromAsync) { _M_Completed.wait(); if (_HasUserException()) { _RethrowUserException(); } else if (_IsCanceled()) {
return canceled; } _ASSERT(_IsCompleted()); return completed; } // 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. if(_M_pTaskCollection->_RunAndWaitPPLTask() == canceled) { _Cancel(true, 0); } } catch(details::_Task_canceled&) { // This could be a _Task_canceled exception flowing through the wait due to a cancellation at a higher level. // // Note that this exception may or may not have already been observed by the task body depending on when the // higher level cancellation happened. _RunAndWait may have thrown the cancellation and aborted the task // before it was picked up -- or it may have happened due to an interruption point in the body. This exception // handler must be consistent with the _PPLTaskHandle's set of handlers. _Cancel(true, 0); throw; } 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. _ASSERT(_IsCanceled()); } 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(), 0); } _ObserveUserException(); throw; } // 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_Completed.wait(); } if (_HasUserException()) { _RethrowUserException(); } else if (_IsCanceled()) { return canceled; } _ASSERT(_IsCompleted()); return completed; } /// <summary> /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. /// </summary> /// <param name="_SynchronousCancel"> /// 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. /// </param> /// <param name="_UserException"> /// Whether an exception other than the internal runtime cancella
tion exceptions caused this cancellation. /// </param> /// <param name="_PropagatedFromAncestor"> /// 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. /// </param> /// <param name="_ExHolder"> /// The exception holder that represents the exception. Only valid when _UserException is set to true. /// </param> virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder, size_t _ContCount) = 0; bool _Cancel(bool _SynchronousCancel, size_t _ContCount) { // Send in a dummy value for exception. It is not used when the first parameter is false. return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder, _ContCount); } bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor, size_t _ContCount) { // This task was canceled because an ancestor task encountered an exception. return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder, _ContCount); } bool _CancelWithException(const std::exception_ptr& _Exception, size_t _ContCount) { // This task was canceled because the task body encountered an exception. _ASSERT(!_HasUserException()); return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception), _ContCount); } 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 _M_exceptionHolder; } bool _ObservesExceptions() { return _M_fObservesExceptions; } void _ObserveUserException() { if (_HasUserException()) { _M_exceptionHolder->_SetExceptionObserved(); } } void _RethrowUserException() { _ASSERT(_HasUserException()); _M_exceptionHolder->_SetExceptionObserved(); std::rethrow_exception(_M_exceptionHolder->_M_storedException); } void _SetScheduledEvent() { _M_Scheduled.set(); } const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() { _ASSERT(_HasUserException()); return _M_exceptionHolder; } bool _IsAsync() { return _M_fFromAsync; } void _SetAsync(bool _Async = true)
{ _M_fFromAsync = _Async; } static void _RunTaskContinuations(std::vector<_TaskProcWithParam>& _Continuations, size_t _Depth) { for (auto it = _Continuations.begin(); it != _Continuations.end(); ++it) { // When this continuation function runs, it will check to see if its ancestor // has canceled or not before scheduling itself for execution it->_M_proc(it->_M_param, _Depth); } _Continuations.clear(); } #if defined(__cplusplus_winrt) static bool _IsSTA() { APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); // // If it failed, it's not a WinRT/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; }
} return false; } template<typename _ReturnType, typename> static void _AsyncInit(typename _Task_ptr<_ReturnType>::_Type _OuterTask, Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_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, don't bother with starting the async operation. The COM reference on // the IAsyncInfo object will be released when all ^references to the operation go out of scope. _ASSERT((_OuterTask->_M_fUnwrappedTask || _OuterTask->_M_pTaskCollection == NULL) && !_OuterTask->_IsCanceled()); if (!_OuterTask->_IsPendingCancel()) { // Note: This is an optimistic check outside the lock. it's possible the task could become canceled while we're in this function. // 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<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _Operation) { if (_Operation->Status == Windows::Foundation::AsyncStatus::Canceled) { // ASYNC TODO: _Cancel may call Cancel on the async op if it is set. Find out whether it is ok to call // IAsyncInfo::Cancel multiple times. _OuterTask->_Cancel(true, 0); } else { try { _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults()); _ASSERT(_Operation->Status == Windows::Foundation::AsyncStatus::Completed); } catch (Platform::COMException^ _E) { _ASSERT(_Operation->Status == Windows::Foundation::AsyncStatus::Error); _OuterTask->_CancelWithException(make_exception_ptr(async_operation_exception(_E)), 0);
} } _Operation->Close(); }); _AsyncOp->Start(); _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); } else { _ASSERT(!_OuterTask->_HasUserException()); // If we were canceled while executing we must respond to it here by completing the cancellation. _OuterTask->_Cancel(true, 0); } }#endif template<typename _ReturnType, typename _InternalReturnType> static void _AsyncInit(typename _Task_ptr<_ReturnType>::_Type _OuterTask, task<_InternalReturnType> _UnwrappedTask) { _ASSERT(_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 funnelling 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 { _ASSERT(_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, 0); } else
{ _OuterTask->_Cancel(true, 0); } } }, false, false); _OuterTask->_SetUnwrappedTask(_UnwrappedTask._GetImpl()); } event _M_Completed; event _M_Scheduled; // Tracks the internal state of the task _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 if we need to marshal the inner parts of an aggregate type like std::vector<T^> or std::pair<T^, size_t>. We only marshal // the contained T^s if we create the vector or pair, such as on a when_any or a when_all operation. bool _M_fRuntimeAggregate; // Set to true when a continuation unwraps a task or async operation. bool _M_fUnwrappedTask; // Set to true for a task-based continuation that is scheduled by the user. All internal (runtime scheduled) task based continuations // should have this set to false. bool _M_fObservesExceptions; // An exception thrown by the task body is captured in an exception holder and it is shared with all its value based continuations. // The exception is 'observed' if the user invokes get()/wait() or schedules a task-based continuation 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, // an unobserved_task_exception is thrown by the runtime. std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; typedef std::vector<_TaskProcWithParam> _ContinuationList; critical_section _M_ContinuationsCritSec; _ContinuationList _M_Continuations; ::Concurrency::details::_PPLTaskChore * _M_pExecutingChore; // The underlying group of tasks as known to the runtime. ::Concurrency::details::_TaskCollection * _M_pTaskCollection; }; /// <summary> /// 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. /// </summary> /// <typeparam name="_ReturnType"> /// The result type of this task. /// </typeparam> /**/ template<typename _ReturnType> struct _Task_impl : public _Task_impl_base {#if defined(__cplusplus_winrt) typedef Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> _AsyncOperationType;#endif _Task_impl() {#if defined(__cplusplus_winrt) _M_unwrapped_async_op = nullptr;#endif } virtual ~_Task_impl() { } virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _DBG_ONLY(_PropagatedFromAncestor), const std::shared_ptr<_ExceptionHolder>& _ExceptionHolder, size_t _ContCount) { _ContinuationList _Continuations; bool _CancelUnwrapped = false; bool _RunContinuations = false; { critical_section::scoped_lock _LockHolder(_M_ContinuationsCritSec); if (_UserException) { _ASSERT(_SynchronousCancel && !_IsCompleted()); // If the state is _Created or _Canceled, the exception has to be coming from an ancestor. _ASSERT((!_IsCreated() && !_IsCanceled()) || _PropagatedFromAncestor); // If the state is _Started or _PendingCancel, the exception cannot be coming from an ancestor. _ASSERT((!_IsStarted() && !_IsPendingCancel()) || !_PropagatedFromAncestor); // We should not be canceled with an exception more than once. _ASSERT(!_HasUserException()); switch (_M_TaskState) { case _Created: // Intentional fall through
case _Started: { // If the task is canceled with exception there is no need to cancel the task collection (if it exists, it should already // have been canceled). _M_exceptionHolder = _ExceptionHolder; } break; case _PendingCancel: { _M_exceptionHolder = _ExceptionHolder; // If cancellation was previously requested, mark the exception as observed. _M_exceptionHolder->_SetExceptionObserved(); } break; case _Canceled: { // If the task has finished cancelling there should not be any continuation records in the array. _ASSERT(_M_Continuations.empty()); return false; } break; case _Completed: // Intentional fall through default: _ASSERT(false); break; } } else { // If the task has a stored exception, cancellation of the task observes the exception. _ObserveUserException(); // 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)) { _ASSERT(!_IsCompleted() || !_HasUserException()); return false; } _ASSERT(!_SynchronousCancel || !_HasUserException()); if (_IsStarted() && !_SynchronousCancel) { // 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 fro
m // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. _M_TaskState = _PendingCancel; if (_M_pTaskCollection != NULL) { _M_pTaskCollection->_Cancel(); } _CancelUnwrapped = _M_unwrapped_task;#if defined(__cplusplus_winrt) if (_M_unwrapped_async_op != nullptr) { _ASSERT(!_CancelUnwrapped); _CancelUnwrapped = true; }#endif } } // It is important to check if this task has an unwrapped task and *not* complete cancellation unless it is also canceled. If an // unwrapped task was linked up, the burden for finalizing cancellation/completion state is on that tasks continuations (we scheduled // a task based continuation off the unwrapped task for this purpose in _AsyncInit). // // Consider the following example: // Outer task OT creates and returns inner task IT. After IT has started executing (_Started), but before OT has returned IT, // the user cancels OT. If OT has no interruption points, the pending cancellation will first be detected in _AsyncInit when OT's // lambda returns. If the user meanwhile does a wait() on OT, _RunAndWait will return a status of 'canceled' causing wait to // invoke a synchronous cancellation on OT. However, we want OT's cancellation to be completed when IT is done executing/cancelling, // since it's already hooked up via _M_unwrapped_task. Ignoring this cancel allows IT to complete the cancellation on OT. if ((_SynchronousCancel || _IsCreated()) && (!_M_unwrapped_task || _M_unwrapped_task->_IsCanceled())) { // The state could be _Created if we're cancelled as the result of an ancestor being canceled or a task completion event // cancellation if (_IsCreated()) { _M_Scheduled.set(); } _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.
_Continuations.swap(_M_Continuations); _RunContinuations = true; } } // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. if (_RunContinuations) { _M_Completed.set(); // If nesting is not too deep, continue traversing the task tree on the stack. Otherwise, ship the work off to an LWT if( _ContCount < 64 ) { _RunTaskContinuations(_Continuations, _ContCount + 1); } else { auto _Func = [_Continuations]() mutable { _RunTaskContinuations(_Continuations, 0); }; _ScheduleLightweightTask(_Func); } } if (_CancelUnwrapped) { if (_M_unwrapped_task) { // The unwrapped task should be canceled after releasing the lock in case it has any task based continuations that could // run inline. _M_unwrapped_task->_Cancel(false, 0); }#if defined(__cplusplus_winrt) else { _ASSERT(_M_unwrapped_async_op != nullptr); // The async op should also be canceled after releasing the lock. Some objects may not be able to process Cancel requests while // the completed handler is executing. _M_unwrapped_async_op->Cancel(); }#endif } return true; } void _FinalizeAndRunContinuations(_ReturnType _Result) { _ContinuationList _Continuations; _M_Result = _Result;
#if defined(__cplusplus_winrt) _M_ResultContext = ::Concurrency::preview::details::_ResultContext<_ReturnType>::_GetContext(_M_fRuntimeAggregate);#endif { // // Hold this lock to ensure continuations being concurrently either get added // to the _M_Continuations vector or wait for the result // critical_section::scoped_lock _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. _ASSERT(!_HasUserException() && !_IsCompleted()); if (_IsCanceled()) { _ASSERT(_M_Continuations.empty()); return; } // If a cancel request slipped through after this task was scheduled, set the state to _Canceled // before scheduling continuations. if (_IsPendingCancel()) { _M_TaskState = _Canceled; } else { _M_TaskState = _Completed; } _M_Completed.set(); _Continuations.swap(_M_Continuations); } _RunTaskContinuations(_Continuations, 0); } // // This method is invoked when the starts executing. The task returns early if this method returns true. // bool _TransitionedToScheduled() { critical_section::scoped_lock _LockHolder(_M_ContinuationsCritSec); if (_IsCanceled()) { return false; }
_ASSERT(_IsCreated()); _M_TaskState = _Started; return true; } /// <summary> /// Helper function to schedule the task on the Task Collection. /// </summary> /// <param name="_Func"> /// The function object to execute. /// </param> /// <param name="_InlineInApartment"> /// Tells the method to inline the function object on the task collection instead of scheduling it for potential /// stealing. This should only be set to true when the continuation context does not allow for the continuation /// to be scheduled on a runtime thread. /// </param> template<typename _Function> void _ScheduleTaskHelper(const _Function& _Func, bool _RunInline = false) { if (_RunInline) { // For chores that execute inline, the scheduled event should be set when the chore starts running - this takes place // in _TaskContinuationParameter::_Continue(). This is to prevent a different thread performing a wait on the task from waiting // on the task collection before the chore is actually added to it via run and wait, and thereby returning from the wait() before // the chore has executed. try { _Func(); } catch (const task_canceled &) { _Cancel(true, 0); } catch (const _Task_canceled &) { // PREVIEW: doesn't really do anything since _Task_canceled is not thrown by the runtime // We should not see _Task_canceled here since this is a top level chore being inlined. _ASSERT(false); } catch(...) { _CancelWithException(std::current_exception(), 0); } } else { {
// Grab the lock so we can check on the cancellation status of this task. If it has already been // canceled, the cancellation has taken care of propagating the cancellation down, we should not // allocate a task collection or chore for this task. A wait on this task will then not have a task // to wait on and can simply return canceled. // If a cancellation happens AFTER this lock releases, then that means a task has been scheduled, // so a wait on the task will enter _RunAndWaitPPLTask, which will ensure the cancellation has completed. critical_section::scoped_lock _LockHolder(_M_ContinuationsCritSec); if (_IsCanceled()) { return; } // TODO: perf optimization -- investigate using suballocator _M_pTaskCollection = new ::Concurrency::details::_TaskCollection(); // Create a PPLTaskHandle for the user function. This is a _PPLTaskChore which is simply an // _Unrealized chore with an extra ref counter for the allocated task collection, above. _M_pExecutingChore = new _PPLTaskHandle<_Function>(_Func, this); } // This special TaskCollection API causes the runtime to delete the task collection completing the // execution of the chore. _M_pTaskCollection->_ScheduleCollectionDeletingTask(_M_pExecutingChore); // Set the event in case anyone is waiting to notify that this task has been scheduled. In the case where we // execute the chore inline, the event should be set after the chore has started (and therefore, finished) // executing. This is fine, since a waiting thread could not have inlined this chore anyway. _SetScheduledEvent(); // If this task going out of scope and the task has already finished scheduling and executing, // destruct the task collection and chore to clean up memory. if (_InterlockedDecrement(&_M_pExecutingChore->_M_collectionRefCount) == 0) { // This deletion removes one of the references on the _Task_impl_base and could initiate // destruction of the task. delete _M_pExecutingChore;
} } } void _ScheduleTask(void * _PData) { auto _PParam = (_TaskExecutionParameter<_ReturnType>*) _PData; // // The parameter is passed to the execution thunk in order to do the right thing. It cannot be deleted inside the body // of the lambda because it holds refs to the task_impl. Deletion of the task_impl will delete the underlying task // collection. The lambda itself must hold reference to the task impl. A capture by value of a shared pointer to the // parameter accomplishes the necessary lifetime semantics. // std::shared_ptr<_TaskExecutionParameter<_ReturnType>> _PShParam(_PParam); _ScheduleTaskHelper([_PShParam]() { _PShParam->_Execute(); }); } // Schedule a continuation to run template <typename _InputType> void _ScheduleContinuationTask(details::_TaskContinuationParameterBase* _PData) { auto _PParam = static_cast<_TaskContinuationParameter<_InputType,_ReturnType>*>(_PData); // // The parameter is passed to the continuation thunk in order to do the right thing. As with _ScheduleTask, it cannot // be deleted inside the body of the lambda because it holds refs to the task_impl. Deletion of the task_impl will delete // the underlying task collection. The lambda itself must hold reference to the task impl. A capture by value of a shared // pointer to the parameter accomplishes the necessary lifetime semantics. // std::shared_ptr<_TaskContinuationParameter<_InputType,_ReturnType>> _PShParam(_PParam); // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different WinRT apartment) if (_PParam->_ContinuationContext._HasCapturedContext()) { _PParam->_ContinuationContext._CallInContext( [_PShParam](){ _PShParam->_Continuation->_ScheduleTaskHelper([_PShParam]() { // This code executes inside _PPLTaskHandle::operator(), inline on the thread that calls _ScheduleTaskHelper _PShParam->_Continue(); }, true);
}); } else { _ScheduleTaskHelper([_PShParam]() { // This code executes inside _PPLTaskHandle::operator() _PShParam->_Continue(); }); } } #if defined (__cplusplus_winrt) void _SetUnwrappedAsyncOp(_AsyncOperationType^ _AsyncOp) { critical_section::scoped_lock _LockHolder(_M_ContinuationsCritSec); // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. if (_IsPendingCancel()) { _ASSERT(!_IsCanceled()); _AsyncOp->Cancel(); } else { _M_unwrapped_async_op = _AsyncOp; } }#endif void _SetUnwrappedTask(typename _Task_ptr<_ReturnType>::_Type _UnwrappedTask) { bool _Cancel = false; { critical_section::scoped_lock _LockHolder(_M_ContinuationsCritSec); // Store the unwrapped task even if the continuation is pending cancel. See comments around this in _CancelAndRunContinuations. // If an unwrapped task happens to be linked at the time a task is synchronously canceled the task should NOT schedule continuations or // take itself into a terminal state, since a continuation on the unwrapped task will do that. _M_unwrapped_task = _UnwrappedTask; if (_IsPendingCancel()) { _Cancel = true; _ASSERT(!_HasUserException() && !_IsCanceled()); } else { _M_unwrapped_task = _UnwrappedTask; } } if (_Cancel)
{ // It is sufficient to cancel the unwrapped task - the exception handling continuation on the unwrapped task will // cancel _Continuation when it is executed. Cancel outside the lock in case we catch the task before it has been scheduled // and execute continuations inline. _UnwrappedTask->_Cancel(false, 0); } } _ReturnType _GetResult() {#if defined(__cplusplus_winrt) return ::Concurrency::preview::details::_ResultContext<_ReturnType>::_GetValue(_M_Result, _M_ResultContext, _M_fRuntimeAggregate);#else // defined(__cplusplus_winrt) return _M_Result;#endif // defined(__cplusplus_winrt) } _ReturnType _M_Result; // this means that the result type must have a public default ctor. typename _Task_ptr<_ReturnType>::_Type _M_unwrapped_task;#if defined(__cplusplus_winrt) _AsyncOperationType^ _M_unwrapped_async_op; _ContextCallback _M_ResultContext;#endif }; // // ASYNC TODO: Usage of std::function for the functions returned in task::_ContinuationStub causes the input and output type names to be replicated in // the template decorated name a total of 14 times. This causes the mangled names for many ConcRT FVT tests to be longer than 4096 // characters. The compiler is presently *CRASHING* when processing such symbols in c2. This is a temporary workaround designed to reduce // the template name length and allow tests to continue to compile with the present factoring. Once the compiler bug is fixed, this should // be restored to utilize a function object. // template<typename _In, typename _Out> class _Workaround_Invoker { public: virtual _Out _Invoke(_In _Inval) =0; // A virtual destructor needs to be defined so that the derived class destructor is invoked when a derived class object is deleted // using a _Workaround_Invoker*. virtual ~_Workaround_Invoker() { } };
template<typename _In, typename _Out, typename _Function> class _Workaround_Function : public _Workaround_Invoker<_In, _Out> { public: _Workaround_Function(const _Function& _Func) : _UserFunc(_Func) { } virtual _Out _Invoke(_In _Inval) { return _UserFunc(_Inval); } private: _Workaround_Function& operator=(const _Workaround_Function&); _Function _UserFunc; }; template<typename _ReturnType> struct _TaskExecutionParameter { typedef typename _NormalizeVoidToUnitType<_ReturnType>::_Type _NormalizedReturnType; typedef _Workaround_Invoker<_TaskExecutionParameter<_ReturnType> *, void> *_InvocationThunk; typedef typename std::function<_NormalizedReturnType()> _NominalFunc; _TaskExecutionParameter(typename _Task_ptr<_NormalizedReturnType>::_Type _T, _InvocationThunk _Func) : _Task(_T), _ExecutionFunction(_Func) { } ~_TaskExecutionParameter() { delete _ExecutionFunction; } void _Execute() { if (_Task->_TransitionedToScheduled()) { _ExecutionFunction->_Invoke(this); } } typename _Task_ptr<_NormalizedReturnType>::_Type _Task; _InvocationThunk _ExecutionFunction; }; struct _TaskContinuationParameterBase {};
template<typename _ReturnType, typename _NewReturnType> struct _TaskContinuationParameter : public _TaskContinuationParameterBase { typedef typename _NormalizeVoidToUnitType<_ReturnType>::_Type _NormalizedReturnType; typedef typename _NormalizeVoidToUnitType<_NewReturnType>::_Type _NewNormalizedReturnType; // typedef std::function<void(_TaskContinuationParameter<_ReturnType, _NewReturnType> *)> _InvocationThunk; typedef _Workaround_Invoker<_TaskContinuationParameter<_ReturnType, _NewReturnType> *, void> *_InvocationThunk; typedef typename _FixFunction<std::function<_NewNormalizedReturnType(_NormalizedReturnType)>>::_FixedFunc _NominalFunc; _TaskContinuationParameter(typename _Task_ptr<_NormalizedReturnType>::_Type _Anc, typename _Task_ptr<_NewNormalizedReturnType>::_Type _Cont, _InvocationThunk _Func, const task_continuation_context& _Context, bool _PassExceptionsToFunc) : _TaskContinuationParameterBase(), _Ancestor(_Anc), _Continuation(_Cont), _ContinuationContext(_Context), _ContinuationFunction(_Func), _ProcessExceptions(_PassExceptionsToFunc) { // If the context has requested deferred capture, resolve it here _ContinuationContext._Resolve(_Ancestor->_IsAsync()); } ~_TaskContinuationParameter() { delete _ContinuationFunction; } void _Continue() { if (_Continuation->_TransitionedToScheduled()) { if (_ContinuationContext._HasCapturedContext()) { // This continuation is executing in a captured context, and was not allowed to be scheduled by one of the runtime's threads. // This means the scheduled event for it has not been set yet. We set the event here. _Continuation->_SetScheduledEvent(); } // _ObservesExceptions returns true if this is a 'user scheduled' task-based continuation. The runtime schedules internal task based // continuations, but they should *never* observe exceptions.
if (_Continuation->_ObservesExceptions()) { _ASSERT(_ProcessExceptions); _Ancestor->_ObserveUserException(); } //_ContinuationFunction(this); _ContinuationFunction->_Invoke(this); } } typename _Task_ptr<_NormalizedReturnType>::_Type _Ancestor; typename _Task_ptr<_NewNormalizedReturnType>::_Type _Continuation; task_continuation_context _ContinuationContext; _InvocationThunk _ContinuationFunction; bool _ProcessExceptions; }; template<typename _ResultType> struct _Task_completion_event_impl { typedef std::vector<typename _Task_ptr<_ResultType>::_Type> _TaskList; _Task_completion_event_impl() : _HasValue(false), _IsCanceled(false) { } bool _HasUserException() { return _ExceptionHolder; } ~_Task_completion_event_impl() { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { _ASSERT(!_HasValue && !_IsCanceled); // Cancel the tasks since the event was never signaled or canceled. (*_TaskIt)->_Cancel(true, 0); } } // We need to protect the loop over the array, so concurrent_vector would not have helped _TaskList _Tasks; critical_section _TaskListCritSec; _ResultType _Value; std::shared_ptr<_ExceptionHolder> _ExceptionHolder; bool _HasValue; bool _IsCanceled; };} // details
#if defined(__cplusplus_winrt)// ASYNC TODO: temporary - replace with Platform::COMException when it is convertible to std::exception_ptr.// This is tracked by Dev11 Bug: 237467class async_operation_exception : public std::exception{public: async_operation_exception(Platform::COMException^ _E): _M_platformException(_E) { } Platform::COMException^ get_platform_exception() { return _M_platformException; } private: Platform::COMException^ _M_platformException;};#endif class task_continuation_context : public details::_ContextCallback{public: // The continuation will be executed in a context that is determined by the ancestor task and the current context. // The default behavior of task continuations is as follows: // Some definitions: // An async task is a task that unwraps a WinRT async operation. // An apartment bound task is a task in the continuation tree of an async task. The apartment bound property // is infectious. That is, if one of the tasks in a when_all or a when_any is an async or apartment bound // task, then when_all task and it's continuations become apartment bound. // Default continuation behavior: // - A continuation off an apartment bound task executes in the apartment where .then was called to schedule that continuation. // - A continuation off a non-apartment bound task is scheduled on a Concurrency Runtime worker thread, in the MTA. 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 _TaskContinuationParameter#else return task_continuation_context();#endif }
#if defined(__cplusplus_winrt) // Creates a task continuation context which completely ignores any marshaling requirements. The continuation will run where // the runtime deems appropriate. static task_continuation_context use_arbitrary() { task_continuation_context _Arbitrary(true); _Arbitrary._Resolve(false); return _Arbitrary; } // Captures the caller's context for WinRT so that callback are made in the right apartment. By default, a callback on a task // wrapping a WinRT async object (or the continuation chain of a WinRT async object) is made in the apartment .then is called in. // However, this default behavior can be changed for async continuations by specifying the continuation context to be 'arbitrary', // and for non-async continuations by specifying the continuation context to be 'current'. static task_continuation_context use_current() { task_continuation_context _Current(true); _Current._Resolve(true); return _Current; }#endif private: task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) { }}; /// <summary>/// The task completion event class, allows users to delay the execution of a task until a condition is/// satisfied, or start a task in response to an external event./// </summary>/// <typeparam name="_ResultType">/// The result type of this task_completion_event./// </typeparam>/**/template<typename _ResultType>class task_completion_event { typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; std::shared_ptr<::Concurrency::preview::details::_Task_completion_event_impl<_ResultType>> _M_Impl;public: template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask template <typename T> friend class task_completion_event;
/// <summary> /// The task completion event constructor. /// </summary> /**/ task_completion_event() : _M_Impl(std::make_shared<::Concurrency::preview::details::_Task_completion_event_impl<_ResultType>>()) { } /// <summary> /// The set the task completion event. /// </summary> /// <param> /// The result to set this event with. /// </param> /// <returns> /// If this call to set actually does set the event, <c>true</c> is returned. If the event has already been set, /// <c>false</c> is returned /// </returns> /**/ 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 (_M_Impl->_HasValue == true || _M_Impl->_IsCanceled == true) { return false; } _TaskList _Tasks; bool _RunContinuations = false; { critical_section::scoped_lock _LockHolder(_M_Impl->_TaskListCritSec); if (_M_Impl->_HasValue == false && _M_Impl->_IsCanceled == false) { _M_Impl->_Value = _Result; _M_Impl->_HasValue = true; _Tasks.swap(_M_Impl->_Tasks); _RunContinuations = true; } } if (_RunContinuations) { for( auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt ) { // 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->_Value); } if (_M_Impl->_HasUserException()) { _M_Impl->_ExceptionHolder.reset(); } return true; } return false; } /// <summary> /// Internal method that cancel the task_completion_event. /// </summary> /**/ bool _CancelInternal(bool _UserException, const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) 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. _ASSERT(!_M_Impl->_HasValue); if (_M_Impl->_IsCanceled == true) { return false; } _TaskList _Tasks; bool _Cancel = false; { critical_section::scoped_lock _LockHolder(_M_Impl->_TaskListCritSec); _ASSERT(!_M_Impl->_HasValue); if (_M_Impl->_IsCanceled == false) { _M_Impl->_IsCanceled = true; _Tasks.swap(_M_Impl->_Tasks); _Cancel = true; } } 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(_ExHolder, true, 0
); } else { (*_TaskIt)->_Cancel(true, 0); } } if (_M_Impl->_HasUserException()) { _M_Impl->_ExceptionHolder.reset(); } } return _Cancel; } /// <summary> /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// </summary> /**/ bool _Cancel() const { // Cancel with the stored exception if one exists. return _CancelInternal(_M_Impl->_HasUserException(), _M_Impl->_ExceptionHolder); } /// <summary> /// Cancel the task_completion_event with the exception provided. Any task created using this event will be canceled /// with the same exception. /// </summary> /**/ bool _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { // We want to make sure that an exception doesn't override a stored exception via this method. If the usage changes such // that this assert starts to fire, an exception from a the tasks in a when_any or when_all may be unobserved. _ASSERT(!_M_Impl->_HasUserException()); return _CancelInternal(true, _ExHolder); } /// <summary> /// 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. /// </summary> /**/
bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { // Storing exceptions on task completion events is an internal only mechanism. if (_M_Impl->_HasValue == true || _M_Impl->_IsCanceled == true || _M_Impl->_HasUserException()) { return false; } critical_section::scoped_lock _LockHolder(_M_Impl->_TaskListCritSec); if (_M_Impl->_HasValue == false && _M_Impl->_IsCanceled == false && !_M_Impl->_HasUserException()) { _M_Impl->_ExceptionHolder = _ExHolder; return true; } return false; } private: /// <summary> /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// </summary> /**/ void _RegisterTask(typename ::Concurrency::preview::details::_Task_ptr<_ResultType>::_Type _TaskParam) { _TaskParam->_SetScheduledEvent(); critical_section::scoped_lock _LockHolder(_M_Impl->_TaskListCritSec); // // Cancellation of and setting exceptions on task completion events is not exposed, and the internal implementation // does not register tasks with events after they are canceled or have an exception stored. // _ASSERT(!_M_Impl->_IsCanceled); _ASSERT(!_M_Impl->_ExceptionHolder); if (_M_Impl->_HasValue) { _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_Value); } else { _M_Impl->_Tasks.push_back(_TaskParam); } }}; /// <summary>/// The task completion event class, allows users to delay the execution of a task until a condition is
/// satisfied, or start a task in response to an external event./// Template specialization for void./// </summary>/**/template<>class task_completion_event<void>{ task_completion_event<::Concurrency::preview::details::_Unit_type> _UnitEvent; public: template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask /// <summary> /// The set the task completion event. /// </summary> /// <returns> /// If this call to set actually does set the event, <c>true</c> is returned. If the event has already been set, /// <c>false</c> is returned /// </returns> /**/ bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { return _UnitEvent.set(::Concurrency::preview::details::_Unit_type()); } /// <summary> /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has /// not already been set. /// </summary> /**/ void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas { _UnitEvent._Cancel(); } /// <summary> /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled /// with the same exception. /// </summary> /**/ void _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { _UnitEvent._Cancel(_ExHolder); } /// <summary> /// Method that stores an exception in the task completion event. Thi
s 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. /// </summary> /**/ bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { return _UnitEvent._StoreException(_ExHolder); } private: /// <summary> /// Register a task with this event. This function is called when a task is constructed using /// a task_completion_event. /// </summary> /**/ void _RegisterTask(::Concurrency::preview::details::_Task_ptr<::Concurrency::preview::details::_Unit_type>::_Type _TaskParam) { _UnitEvent._RegisterTask(_TaskParam); }}; /// <summary>/// The PPL task class./// </summary>/// <typeparam name="_ReturnType">/// The result type of this task./// </typeparam>/**/template<typename _ReturnType>class task{public: typedef _ReturnType _TaskType; /// <summary> /// Constructor for a PPL task. /// </summary> /// <param name="_Func"> /// The function for this PPL task to execute. /// </param> /// <param name="_Event"> /// A task_completion_event with which to create this task. The task will be set as completed /// when the task_completion_event is set. /// </param> /// <remarks> /// The default constructor for a task is only present in order to allow tasks to be used within containers.
/// Users should never create a default constructed task because it is not usable: you cannot run anything, /// continue any work, or wait on it. /// </remarks> /**/ task() : _M_Impl(NULL) { // The default constructor should create a task with a NULL impl. This is a signal that the // task is not usable and should throw if any wait(), get() or then() APIs are used. } /// <summary> /// Create an underlying task implementation. /// </summary> /// <remarks> /// In one instance (the then() API) we need to default construct a task with an Impl, since /// that is the continuation task being returned. /// </remarks> /**/ void _CreateImpl() { _M_Impl = ::Concurrency::preview::details::_Task_ptr<_ReturnType>::make(); } /// <summary> /// Set the implementation of the task to be the supplied implementaion. /// </summary> /// <remarks> /// When a continuation that takes a task<T> is scheduled on a task<T>, this method is /// used to generate a task with the same implementataion as the ancestor task, effectively /// incrementing the reference on the shared_ptr in the ancestor task. /// </remarks> /**/ void _SetImpl(typename ::Concurrency::preview::details::_Task_ptr<_ReturnType>::_Type _Impl) { _ASSERT(_M_Impl == NULL); _M_Impl = _Impl; } /// <summary> /// Determine whether the task is a task that wraps an async object or is descended from the continuation chain of such a task. /// </summary> bool _IsAsync() const { return _GetImpl()->_IsAsync();
} /// <summary> /// Sets a property determining whether the task is a task that wraps an async object or is descended from the continuation chain of such a task. /// </summary> void _SetAsync(bool _Async = true) { _GetImpl()->_SetAsync(_Async); } /// <summary> /// Return the underlying implementation for this task. /// </summary> /// <returns> /// The underlying implementation for the task. /// </returns> /**/ typename ::Concurrency::preview::details::_Task_ptr<_ReturnType>::_Type _GetImpl() const { return _M_Impl; } template <typename T> friend class task; private: // The underlying implementation for this task typename ::Concurrency::preview::details::_Task_ptr<_ReturnType>::_Type _M_Impl; /// <summary> /// Static function that executes a continuation function. This function is recorded by a parent task implementation /// when a continuation is created in order to execute later. /// </summary> /// <typeparam name="_NewReturnType"> /// The return type of the continuation. /// </typeparam> /// <param name="_PData"> /// A void* pointing to a _TaskContinationParameter, which contains pointers to this (parent) task implementation /// as well as the the continuation task implementation. /// </param> /**/ template <typename _NewReturnType> static void __cdecl _RunContinuation(details::_TaskContinuationParameterBase* _PData, size_t _InvocationNestingLevel) { auto _PParam = static_cast<::Concurrency::preview::details::_TaskContinuationParameter<_ReturnType,_NewReturnType>*>(_PData); if (_PParam->_Ancestor->_IsCanceled() && !_PParam->_ProcessExceptions)
{ if (_PParam->_Ancestor->_HasUserException()) { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _PParam->_Continuation->_CancelWithExceptionHolder(_PParam->_Ancestor->_GetExceptionHolder(), true, _InvocationNestingLevel); } else { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _PParam->_Continuation->_Cancel(true, _InvocationNestingLevel); } // Delete the parameter to free up the reference for this task and the ancestor. delete _PParam; } 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). _ASSERT(_PParam->_Ancestor->_IsCompleted() || _PParam->_ProcessExceptions); if (!_PParam->_Continuation->_IsCanceled()) { _PParam->_Continuation->_ScheduleContinuationTask<_ReturnType>(_PParam); } else { delete _PParam; } } } #if defined(__cplusplus_winrt) /// <summary> /// Initializer invoked by the constructor for a PPL task. /// </summary> /// <remarks> /// Create a task from an asynchronous operation IAsyncOperation<T> /// </remarks> /**/ void _TaskInitAsyncOp(Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _AsyncOp) { //typedef typename details::_ValueTypeOrRefType<_ReturnType>::_Value _Ty; _M_Impl->_M_fFromAsync = true;
_M_Impl->_SetScheduledEvent(); // 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); }#endif template<typename _InternalReturnType, typename _Function> void _TaskInitWithFunctor(const _Function& _Func) { typedef ::Concurrency::preview::details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits; _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; auto _PParam = new ::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType>( _GetImpl(), _InitStub<_InternalReturnType, _Function>(_Func, _Async_type_traits::_AsyncKind()) ); _M_Impl->_ScheduleTask(_PParam); } template<typename _InternalReturnType, typename _Function> //static std::function<void(::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void> * _InitStub(const _Function& _Func, /* returns IAsync */ details::_TypeSelectorNoAsync) { // // Overload 0: returns _InternalReturnType // // This is the most basic task with no unwrapping // auto _StubFunc = [=](::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *_PParam){ _PParam->_Task->_FinalizeAndRunContinuations(_Init_func_transformer<_InternalReturnType>::_Perform(_Func)()); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Co
ncurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _Function> //static std::function<void(::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void> * _InitStub(const _Function& _Func, /* returns IAsync */ details::_TypeSelectorAsyncOperationOrTask) { // // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only uder /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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *_PParam){ details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_PParam->_Task, _Func()); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _Function> //static std::function<void(::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void> * _InitStub(const _Function& _Func, /* returns IAsync */ details::_TypeSelectorAsyncAction) { // // Overload 2: returns IAsyncAction^ // // This is task whose functor returns an async action which will be unwrapped for continuation // auto _StubFunc = [=](::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *_PParam){ details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_PParam->_Task, ref new details::_IAsyncActionToAsyncOperationConverter(_Func()));
}; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _Function> //static std::function<void(::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void> * _InitStub(const _Function& _Func, /* returns IAsync */ details::_TypeSelectorAsyncOperationWithProgress) { // // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^ // // This is task whose functor returns an async operation with progress which will be unwrapped for continuation // auto _StubFunc = [=](::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *_PParam){ auto _OpWithProgress = _Func(); typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_PParam->_Task, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,_ProgressType>(_OpWithProgress)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _Function> //static std::function<void(::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void> * _InitStub(const _Function& _Func, /* returns IAsync */ details::_TypeSelectorAsyncActionWithProgress) { // // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^ // // This is task whose functor returns an async action with progress which will be unwrapped for continuation
// auto _StubFunc = [=](::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *_PParam){ auto _OpWithProgress = _Func(); typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_PParam->_Task, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskExecutionParameter<_InternalReturnType> *, void, decltype(_StubFunc)>(_StubFunc); } /// <summary> /// Initializer invoked by the constructor for a PPL task. /// </summary> /// <param name="_Event"> /// A task_completion_event with which to create this task. The task will be set as completed /// when the task_completion_event is set. /// </param> /**/ void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) { _Event._RegisterTask(_M_Impl); } #if defined(__cplusplus_winrt) void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>^ _AsyncOp) { _TaskInitAsyncOp(_AsyncOp); } /// <summary> /// Constructor for a PPL task. /// </summary> /// <remarks> /// Create a task from an asynchronous operation with progress IAsyncOperationWithProgress<T> /// </remarks> /**/ template<typename _Progress> void _TaskInitNoFunctor(Windows::Foundation::IAsyncOperationWithProgress<typename details::_ValueTypeOrRefType<_ReturnType>::_Value, _Progress>^ _AsyncOp) { _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<typename details::_ValueTypeOrRefType<_ReturnType>::_Value
, _Progress>(_AsyncOp)); } #endif // _TaskInitMaybeFunctor overload for non-callable objects. template<typename _Function> void _TaskInitMaybeFunctor(_Function& _Func, std::true_type) { _TaskInitWithFunctor<_ReturnType, _Function>(_Func); } // _TaskInitMaybeFunctor overload for non-callable objects. template<typename _Ty> void _TaskInitMaybeFunctor(_Ty& _Param, std::false_type) { _TaskInitNoFunctor(_Param); } public: /// <summary> /// The non-default constructor of the task class. See _TaskInitNoFunctor overloads for the /// various parameter types the constructor can accept. /// </summary> template<typename _Ty> explicit task(_Ty _Param) : _M_Impl(::Concurrency::preview::details::_Task_ptr<_ReturnType>::make()) { _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// <summary> /// Destructor for a PPL task. /// </summary> /**/ ~task() { } /// <summary> /// A utility function for executing task functions which helps to manage different types. Since void tasks can accept continuations that /// take task<void>, the input type is templatized. /// </summary> /// <typeparam name="_InpType"> /// The output type of the function. /// </typeparam> /// <typeparam name="_OutType"> /// The output type of the function. /// </typeparam> /**/ template<typename _InpType, typename _OutType> class _Continuation_func_transformer {
public: static typename ::Concurrency::preview::details::_TaskContinuationParameter<_InpType,_OutType>::_NominalFunc _Perform(typename details::_FixFunction<std::function<_OutType(_InpType)>>::_FixedFunc _Func) { return _Func; } }; /// <summary> /// A utility function for executing task functions which helps to manage different types. Since void tasks can accept continuations that /// take task<void>, the input type is templatized. /// </summary> /// <typeparam name="_OutType"> /// The output type of the function. /// </typeparam> /**/ template<typename _OutType> class _Continuation_func_transformer<void, _OutType> { public: static typename ::Concurrency::preview::details::_TaskContinuationParameter<void,_OutType>::_NominalFunc _Perform(std::function<_OutType(void)> _Func) { return ::Concurrency::preview::details::_MakeUnitToTFunc<_OutType>(_Func); } }; /// <summary> /// A utility function for executing task functions which helps to manage different types. Since void tasks can accept continuations that /// take task<void>, the input type is templatized. /// </summary> /// <typeparam name="_OutType"> /// The output type of the function. /// </typeparam> /**/ template<typename _InType> class _Continuation_func_transformer<_InType, void> { public: static typename ::Concurrency::preview::details::_TaskContinuationParameter<_InType,void>::_NominalFunc _Perform(typename details::_FixFunction<std::function<void(_InType)>>::_FixedFunc _Func) { return ::Concurrency::preview::details::_MakeTToUnitFunc<_InType>(_Func); } }; /// <summary> /// A utility function for executing task functions which helps to ma
nage different types. Since void tasks can accept continuations that /// take task<void>, the input type is templatized. /// </summary> /**/ template<> class _Continuation_func_transformer<void, void> { public: static ::Concurrency::preview::details::_TaskContinuationParameter<void,void>::_NominalFunc _Perform(std::function<void(void)> _Func) { return ::Concurrency::preview::details::_MakeUnitToUnitFunc(_Func); } }; template<typename _RetType> class _Init_func_transformer { public: static typename ::Concurrency::preview::details::_TaskExecutionParameter<_RetType>::_NominalFunc _Perform(std::function<_RetType(void)> _Func) { return _Func; } }; template<> class _Init_func_transformer<void> { public: static ::Concurrency::preview::details::_TaskExecutionParameter<void>::_NominalFunc _Perform(std::function<void(void)> _Func) { return ::Concurrency::preview::details::_MakeVoidToUnitFunc(_Func); } }; /// <summary> /// 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. /// </summary> /// <typeparam name="_FuncInputType"> /// The input type of the task. /// </typeparam> /// <typeparam name="_FuncOutputType"> /// The output type of the task. /// </typeparam> /**/ template<typename _FuncInputType, typename _FuncOutputType> static void _ScheduleContinuation(::Concurrency::preview::details::_TaskContinuationParameter<_FuncInputType,_FuncOutputType> *_PParam)
{ enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; auto _Ancestor = _PParam->_Ancestor; _ASSERT (_Ancestor != nullptr); // 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 { critical_section::scoped_lock _LockHolder(_Ancestor->_M_ContinuationsCritSec); if (_Ancestor->_IsCompleted() || (_Ancestor->_IsCanceled() && _PParam->_ProcessExceptions)) { _Do = _Schedule; } else if (_Ancestor->_IsCanceled()) { if (_Ancestor->_HasUserException()) { _Do = _CancelWithException; } else { _Do = _Cancel; } } else { _Ancestor->_M_Continuations.push_back(details::_TaskProcWithParam(_RunContinuation<_FuncOutputType>,_PParam)); } } // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of // async tasks may execute inline. switch (_Do) { case _Schedule: { _PParam->_Continuation->_ScheduleContinuationTask<_FuncInputType>(_PParam); break; } case _Cancel: { // If the ancestor was canceled, then your own execution should be canceled. // This traverses down the tree to cancel it. _PParam->_Continuation->_Cancel(true, 0); // Delete the parameter to free up the reference for this task and the ancestor
delete _PParam; break; } case _CancelWithException: { // If the ancestor encountered an exception, transfer the exception to the continuation // This traverses down the tree to propagate the exception. _PParam->_Continuation->_CancelWithExceptionHolder(_Ancestor->_GetExceptionHolder(), true, 0); // Delete the parameter to free up the reference for this task and the ancestor delete _PParam; } case _Nothing: default: break; } } /// <summary> /// Add a continuation task to this task. The continuation will execute when this task completes, and its function /// will be presented the output of this task's function. /// </summary> /// <typeparam name="_Function"> /// The type of the function object that will be invoked by this task. /// </typeparam> /// <param name="_Func"> /// The continuation function to execute when this task completes. This continuation function must take as input the /// output of this parent task that it is continuing from. /// </param> /// <returns> /// A new task which will be scheduled for execution when this current task completes. The new task's type will /// be the output of the function <c>_Func</c> /// </returns> /**/ template<typename _Function> auto then(const _Function& _Func) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { return _ThenImpl<_ReturnType, _Function>(_Func, task_continuation_context::use_default()); } #if defined(__cplusplus_winrt) /// <summary> /// Add a continuation task to this task. The continuation will execute when this task completes, and its function /// will be presented the output of this task's function. /// </summary> /// <typeparam name="_Function">
/// The type of the function object that will be invoked by this task. /// </typeparam> /// <param name="_Func"> /// The continuation function to execute when this task completes. This continuation function must take as input the /// output of this parent task that it is continuing from. /// </param> /// <param name="_ContinuationContext"> /// A variable that specifies where the continuation should execute. /// </param> /// <returns> /// A new task which will be scheduled for execution when this current task completes. The new task's type will /// be the output of the function <c>_Func</c> /// </returns> /**/ template<typename _Function> auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { return _ThenImpl<_ReturnType, _Function>(_Func, _ContinuationContext); }#endif /// <summary> /// An internal version of then that takes additional flags. /// </summary> /**/ template<typename _Function> auto _Then(const _Function& _Func, bool _ObservesExceptions, bool _Aggregating) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType { return _ThenImpl<_ReturnType, _Function>(_Func, task_continuation_context::use_default(), _ObservesExceptions, _Aggregating); } /// <summary> /// The one and only implementation of then for void and non-void tasks /// </summary> /**/ template<typename _InternalReturnType, typename _Function> auto _ThenImpl(const _Function& _Func, const task_continuation_context& _ContinuationContext, bool _ObservesExceptions = true, bool _Aggregating = false) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType { if (_M_Impl == NULL) { throw invalid_operation("then() cannot be called on a default con
structed task."); } typedef ::Concurrency::preview::details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; typedef ::Concurrency::preview::details::_TaskTypeTraits<_Function_type_traits::_FuncRetType> _Async_type_traits; typedef _Async_type_traits::_TaskRetType _TaskType; task<_TaskType> _ContinuationTask; _ContinuationTask._CreateImpl(); _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); _ContinuationTask._GetImpl()->_M_fRuntimeAggregate = _Aggregating; _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; _ContinuationTask._GetImpl()->_M_fObservesExceptions = _Function_type_traits::_Takes_task() && _ObservesExceptions; auto _PParam = new ::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType>( _GetImpl(), _ContinuationTask._GetImpl(), _ContinuationStub<_InternalReturnType, _TaskType, _Function>(_Func, _Function_type_traits::_Takes_task(), _Async_type_traits::_AsyncKind()), _ContinuationContext, _Function_type_traits::_Takes_task() ); _ScheduleContinuation(_PParam); return _ContinuationTask; } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::false_type, /* returns IAsync */ details::_TypeSelectorNoAsync) { // // Overload 0-0: _InternalReturnType -> _TaskType // // This is a straight task continuation which simply invokes its target with the ancestor's completion argument // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ _PParam->_Continuation->_FinalizeAndRunContinuations(_Continuation_func_transformer<_InternalReturnT
ype, _TaskType>::_Perform(_Func)(_PParam->_Ancestor->_GetResult())); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::false_type, /* returns IAsync */ details::_TypeSelectorAsyncOperationOrTask) { // // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only uder /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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef ::Concurrency::preview::details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>( _PParam->_Continuation, _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_Func)(_PParam->_Ancestor->_GetResult()) ); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } #if defined(__cplusplus_winrt) template<typename _InternalReturnType, typename _TaskType, typename _Func
tion> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* taskes task */ std::false_type, /* returns IAsync */ details::_TypeSelectorAsyncAction) { // // Overload 0-2: _InternalReturnType -> IAsyncAction^ // // This is a straight task continuation which returns an async action which will be unwrapped for continuation // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef ::Concurrency::preview::details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>( _PParam->_Continuation, ref new details::_IAsyncActionToAsyncOperationConverter( _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_Func)(_PParam->_Ancestor->_GetResult()))); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* taskes task */ std::false_type, /* returns IAsync */ details::_TypeSelectorAsyncOperationWithProgress) { // // 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 // auto _StubFunc = [=]
(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef ::Concurrency::preview::details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; auto _OpWithProgress = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_Func)(_PParam->_Ancestor->_GetResult()); typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>( _PParam->_Continuation, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_TaskType,_ProgressType>(_OpWithProgress)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* taskes task */ std::false_type, /* returns IAsync */ details::_TypeSelectorAsyncActionWithProgress) { // // 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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef ::Concurrency::preview::details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; auto _OpWithProgress = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_Func)(_PParam->_Ancestor->_GetResult()); typedef details::_GetProgressType
<decltype(_OpWithProgress)>::_Value _ProgressType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>( _PParam->_Continuation, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } #endif template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::true_type, /* returns IAsync */ details::_TypeSelectorNoAsync) { // // Overload 1-0: task<_InternalReturnType> -> _TaskType // // This is an exception handling type of continuation which takes the task rather than the task's result. // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef task<_InternalReturnType> _FuncInputType; task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(_PParam->_Ancestor); _PParam->_Continuation->_FinalizeAndRunContinuations(_Continuation_func_transformer<_FuncInputType, _TaskType>::_Perform(_Func)(_ResultTask)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency
::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::true_type, /* returns IAsync */ details::_TypeSelectorAsyncOperationOrTask) { // // 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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(_PParam->_Ancestor); details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>(_PParam->_Continuation, _Func(_ResultTask)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } #if defined(__cplusplus_winrt) template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::true_type, /* returns IAsync */ details::_TypeSelectorAsyncAction) { // // 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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(_PParam->_Ancestor); details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>(_PParam->_Continuation, ref new details::_IAsyncActionToAsyncOperationConverter(_Func(_ResultTask))); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::true_type, /* returns IAsync */ details::_TypeSelectorAsyncOperationWithProgress) { // // 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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(_PParam->_Ancestor); auto _OpWithProgress = _Func(_ResultTask); typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _Norm
alizedTaskType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>(_PParam->_Continuation, ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_TaskType,_ProgressType>(_OpWithProgress)); }; // return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } template<typename _InternalReturnType, typename _TaskType, typename _Function> // std::function<void(::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *)> static ::Concurrency::preview::details::_Workaround_Invoker<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void> * _ContinuationStub(const _Function& _Func, /* takes task */ std::true_type, /* returns IAsync */ details::_TypeSelectorAsyncActionWithProgress) { // // 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 // auto _StubFunc = [=](::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *_PParam){ // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. task<_InternalReturnType> _ResultTask; _ResultTask._SetImpl(_PParam->_Ancestor); auto _OpWithProgress = _Func(_ResultTask); typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType; typedef details::_NormalizeVoidToUnitType<_TaskType>::_Type _NormalizedTaskType; details::_Task_impl_base::_AsyncInit<_NormalizedTaskType,_TaskType>(_PParam->_Continuation, ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress)); };
// return _StubFunc; return new ::Concurrency::preview::details::_Workaround_Function<::Concurrency::preview::details::_TaskContinuationParameter<_InternalReturnType, _TaskType> *, void, decltype(_StubFunc)>(_StubFunc); } #endif /// <summary> /// Wait on the task to complete. If the task is still in the process of executing, a /// call to wait can potentially inline work. /// </summary> /// <returns> /// A <c>task_status</c> which can be either <c>completed</c> or <c>canceled</c>. /// </returns> /**/ task_status wait() const { if (_M_Impl == NULL) { throw invalid_operation("wait() cannot be called on a default constructed task."); } return _M_Impl->_Wait(true); } /// <summary> /// Return the result of this task. If the task has not yet completed, this will wait on the /// task to complete. If the task is still in the process of executing, a call to wait can /// potentially inline work. /// </summary> /// <returns> /// The output of the task. /// </returns> /// <remarks> /// If the task is canceled, a call to get will throw an <c>task_canceled</c> exception. /// </remarks> /**/ _ReturnType get() const { if (_M_Impl == NULL) { throw invalid_operation("get() cannot be called on a default constructed task."); } if (_M_Impl->_Wait(false) == canceled) { throw task_canceled();
} return _M_Impl->_GetResult(); } /// <summary> /// Checks whether the task has been canceled /// </summary> /// <returns> /// If the task is canceled, <c>true</c> is returned, otherwise, <c>false</c>. /// </returns> /**/ bool is_canceled() const { if (_M_Impl == NULL) { throw invalid_operation("is_canceled() cannot be called on a default constructed task."); } return _M_Impl->_IsPendingCancel() || _M_Impl->_IsCanceled(); } /// <summary> /// Compare two tasks. /// </summary> /// <returns> /// If the tasks point to the same underlying implementation, <c>true</c> is returned, otherwise, <c>false</c>. /// </returns> /**/ bool operator==(const task<_ReturnType>& _Rhs) const { return (_M_Impl == _Rhs._M_Impl); } /// <summary> /// Compare two tasks /// </summary> /// <returns> /// If the tasks point to the same underlying implementation, <c>false</c> is returned, otherwise, <c>true</c>. /// </returns> /**/ bool operator!=(const task<_ReturnType>& _Rhs) const { return !operator==(_Rhs); } }; namespace details{ // Utility method for dealing with void functions
inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function<void(void)>& _Func) { return [=]() -> _Unit_type { _Func(); return _Unit_type(); }; } template <typename _Type> std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func) { return [=](_Unit_type) -> _Type { return _Func(); }; } template <typename _Type> typename _FixFunction<std::function<_Unit_type(_Type)>>::_FixedFunc _MakeTToUnitFunc(const typename _FixFunction<std::function<void(_Type)>>::_FixedFunc& _Func) { return [=](_Type t) -> _Unit_type { _Func(t); return _Unit_type(); }; } inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function<void(void)>& _Func) { return [=](_Unit_type) -> _Unit_type { _Func(); return _Unit_type(); }; } #if defined(__cplusplus_winrt) /// <summary> /// Base converter class for converting asynchronous interfaces to IAsyncOperation /// </summary> /**/ template<typename _AsyncOperationType, typename _CompletionHandlerType, typename _Result> ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result> { _AsyncOperationType _M_asyncInfo; Windows::Foundation::AsyncOperationCompletedHandler<_Result>^ _M_CompletedHandler; _AsyncInfoImpl( _AsyncOperationType _asyncInfo ) : _M_asyncInfo(_asyncInfo) {} virtual void Cancel() { _M_asyncInfo->Cancel(); } virtual void Close() { _M_asyncInfo->Close(); } virtual void Start() { _M_asyncInfo->Start(); } property Windows::Foundation::HResult ErrorCode { Windows::Foundation::HResult get() { return _M_asyncInfo->ErrorCode;
} } property UINT Id { UINT get() { return _M_asyncInfo->Id; } } property Windows::Foundation::AsyncStatus Status { Windows::Foundation::AsyncStatus get() { return _M_asyncInfo->Status; } } virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); } 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->Completed = ref new _CompletionHandlerType([&](_AsyncOperationType) { _M_CompletedHandler->Invoke(this); }); } } }; /// <summary> /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress<T> into IAsyncOperation<T> /// </summary> /**/ template<typename _Result, typename _Progress> ref struct _IAsyncOperationWithProgressToAsyncOperationConverter : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>,
_Result> { _IAsyncOperationWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^ _Operation) : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result,_Progress>^, Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result,_Progress>, _Result>(_Operation) {} virtual _Result GetResults() { return _M_asyncInfo->GetResults(); } }; /// <summary> /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> /// </summary> /**/ ref struct _IAsyncActionToAsyncOperationConverter : _AsyncInfoImpl<Windows::Foundation::IAsyncAction^, Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type> { _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction^ _Operation) : _AsyncInfoImpl<Windows::Foundation::IAsyncAction^, Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type>(_Operation) {} virtual details::_Unit_type GetResults() { // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. _M_asyncInfo->GetResults(); return details::_Unit_type(); } }; /// <summary> /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> /// </summary> /**/ template<typename _Progress> ref struct _IAsyncActionWithProgressToAsyncOperationConverter : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress>^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type> { _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ _Action) : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Pro
gress>^, Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type>(_Action) {} virtual details::_Unit_type GetResults() { // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. _M_asyncInfo->GetResults(); return details::_Unit_type(); } }; #endif} /// <summary>/// The PPL task class. Explicit specialization for void./// </summary>/**/template<>class task<void>{ // This is the task that will execute the void function task<::Concurrency::preview::details::_Unit_type> _UnitTask; public: typedef void _TaskType; friend class task<void>; template <typename T> friend class task; template <typename T> friend class task_completion_event; private: void _TaskInitNoFunctor(task_completion_event<void>& _Event) { _UnitTask._TaskInitNoFunctor(_Event._UnitEvent); } #if defined(__cplusplus_winrt) void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction^ _AsyncAction) { _UnitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction)); } template<typename _P> void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P>^ _AsyncActionWithProgress) { _UnitTask._TaskInitAsyncOp(ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress)); }#endif
// _TaskInitMaybeFunctor overload for callable objects. template<typename _Function> void _TaskInitMaybeFunctor(_Function& _Func, std::true_type) { _UnitTask._TaskInitWithFunctor<void, _Function>(_Func); } // _TaskInitMaybeFunctor overload for non-callable objects. template<typename _T> void _TaskInitMaybeFunctor(_T& _Param, std::false_type) { _TaskInitNoFunctor(_Param); } public: /// <summary> /// The one and only non-default constructor of the task class. See _TaskInitNoFunctor overloads for the /// various parameter types the constructor can accept /// </summary> /**/ template<typename _Ty> explicit task(_Ty _Param) : _UnitTask() { _UnitTask._CreateImpl(); _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param,0)); } /// <summary> /// Constructor for a PPL task. /// </summary> /// <param name="_Func"> /// The function for this PPL task to execute. /// </param> /// <param name="_Event"> /// A task_completion_event with which to create this task. The task will be set as completed /// when the task_completion_event is set. /// </param> /// <remarks> /// The default constructor for a task is only present in order to allow tasks to be used within containers. /// Users should never create a default constructed task because it is not usable: you cannot run anything, /// continue any work, or wait on it. /// </remarks> /**/ task() : _UnitTask() {} /// <summary> /// Create an underlying task implementation. /// </summary> /// <remarks> /// In one instance (the then() API) we need to default construct a t
ask with an Impl, since /// that is the continuation task being returned. /// </remarks> /**/ void _CreateImpl() { _UnitTask._CreateImpl(); } /// <summary> /// Return the underlying implementation for this task. /// </summary> /// <returns> /// The underlying implementation for the task. /// </returns> /**/ ::Concurrency::preview::details::_Task_ptr<::Concurrency::preview::details::_Unit_type>::_Type _GetImpl() const { return _UnitTask._M_Impl; } /// <summary> /// Set the implementation of the task to be the supplied implementaion. /// </summary> /// <remarks> /// When a continuation that takes a task<T> is scheduled on a task<T>, this method is /// used to generate a task with the same implementataion as the ancestor task, effectively /// incrementing the reference on the shared_ptr in the ancestor task. /// </remarks> /**/ void _SetImpl(::Concurrency::preview::details::_Task_ptr<::Concurrency::preview::details::_Unit_type>::_Type _Impl) { _UnitTask._SetImpl(_Impl); } /// <summary> /// Determine whether the task is a task that wraps an async object or is descended from the continuation chain of such a task. /// </summary> bool _IsAsync() const { return _UnitTask._IsAsync(); } /// <summary> /// Sets a property determining whether the task is a task that wraps an async object or is descended from the continuation chain of such a task. /// </summary> void _SetAsync(bool _Async = true)
{ _UnitTask._SetAsync(_Async); } /// <summary> /// Add a continuation task to this task. The continuation will execute when this task completes, and its function /// will be presented the output of this task's function. /// </summary> /// <typeparam name="_Function"> /// The type of the function object that will be invoked by this task. /// </typeparam> /// <param name="_Func"> /// The continuation function to execute when this task completes. This continuation function must take as input the /// output of this parent task that it is continuing from. /// </param> /// <returns> /// A new task which will be scheduled for execution when this current task completes. The new task's type will /// be the output of the function <c>_Func</c> /// </returns> /**/ template<typename _Function> auto then(const _Function& _Func) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { return _UnitTask._ThenImpl<void, _Function>(_Func, task_continuation_context::use_default()); } #if defined(__cplusplus_winrt) /// <summary> /// Add a continuation task to this task. The continuation will execute when this task completes, and its function /// will be presented the output of this task's function. /// </summary> /// <typeparam name="_Function"> /// The type of the function object that will be invoked by this task. /// </typeparam> /// <param name="_Func"> /// The continuation function to execute when this task completes. This continuation function must take as input the /// output of this parent task that it is continuing from. /// </param> /// <param name="_ContinuationContext"> /// A variable that specifies where the continuation should execute. /// </param> /// <returns> /// A new task which will be scheduled for execution when this current task completes. The new task's type will /// be the output of the function <c>_Func</c> /// </returns>
/**/ template<typename _Function> auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { return _UnitTask._ThenImpl<void, _Function>(_Func, _ContinuationContext); }#endif /// <summary> /// An internal version of then that takes additional flags. /// </summary> /**/ template<typename _Function> auto _Then(const _Function& _Func, bool _ObservesExceptions, bool _Aggregating) const -> typename ::Concurrency::preview::details::_ContinuationTypeTraits<_Function, void>::_TaskOfType { return _UnitTask._ThenImpl<void, _Function>(_Func, task_continuation_context::use_default(), _ObservesExceptions, _Aggregating); } /// <summary> /// Wait on the task to complete. If the task is still in the process of executing, a /// call to wait can potentially inline work. /// </summary> /// <returns> /// A <c>task_status</c> which can be either <c>completed</c> or <c>canceled</c>. /// </returns> /**/ task_status wait() const { return _UnitTask.wait(); } /// <summary> /// Checks whether the task has been canceled. /// </summary> /// <returns> /// If the task is canceled, <c>true</c> is returned, otherwise, <c>false</c>. /// </returns> /**/ bool is_canceled() const { return _UnitTask.is_canceled(); } /// <summary> /// Return the result of this task. If the task has not yet completed
, this will wait on the /// task to complete. If the task is still in the process of executing, a call to wait can /// potentially inline work. /// </summary> /// <remarks> /// If the task is canceled, a call to get will throw an <c>invalid_operation</c> exception. /// </remarks> /**/ void get() const { _UnitTask.get(); } /// <summary> /// Compare two tasks. /// </summary> /// <returns> /// If the tasks point to the same underlying implementation, <c>true</c> is returned, otherwise, <c>false</c>. /// </returns> /**/ bool operator==(const task<void>& _Rhs) const { return (_UnitTask == _Rhs._UnitTask); } /// <summary> /// Compare two tasks. /// </summary> /// <returns> /// If the tasks point to the same underlying implementation, <c>false</c> is returned, otherwise, <c>true</c>. /// </returns> /**/ bool operator!=(const task<void>& _Rhs) const { return !operator==(_Rhs); }}; #if defined(__cplusplus_winrt) namespace details{ template<typename _ProgressType> class _ProgressDispatcherBase { public: virtual ~_ProgressDispatcherBase() { }
virtual void _Report(const _ProgressType& _Val) = 0; }; template<typename _ProgressType, typename _ClassPtrType> class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> { task_continuation_context _M_Ct; task<void> _Task; public: virtual ~_ProgressDispatcher() { } _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr), _M_Ct(task_continuation_context::use_current()), _Task([](){}) { } virtual void _Report(const _ProgressType& _Val) {/* _Task.then([=]() { _M_ptr->_FireProgress(_Val); }, _M_Ct);*/ _M_Ct._CallInContext( [=](){ _M_ptr->_FireProgress(_Val); }); } private: _ClassPtrType _M_ptr; };} /// <summary>/// The progress reporter class represents a sink for reporting progress notifications of a specific type. Each progress_reporter object is bound/// to a particular asynchronous action or operation./// </summary>/// <typeparam name="_ProgressType">/// The payload type of each progress notification reported through the progress reporter./// </typeparam>/**/template<typename _ProgressType>class progress_reporter{ typedef std::shared_ptr<details::_ProgressDispatcherBase<_ProgressType>> _PtrType; public:
template<typename _ClassPtrType> 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; } /// <summary> /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. /// </summary> /// <param name="_Val") /// The ppayload to report through a progress notification. /// </param> /**/ void report(const _ProgressType& _Val) const { _M_dispatcher->_Report(_Val); } private: _PtrType _M_dispatcher;}; namespace details{ // // maps internal definitions for AsyncStatus and defines states that are not client visible // enum _AsyncStatusInternal { // client visible states (must match AsyncStatus exactly) _AsyncCreated = 0, // ::Windows::Foundation::AsyncStatus::Created, _AsyncStarted = 1, // ::Windows::Foundation::AsyncStatus::Started, _AsyncCompleted = 2, // ::Windows::Foundation::AsyncStatus::Completed, _AsyncCancelled = 3, // ::Windows::Foundation::AsyncStatus::Canceled, _AsyncError = 4, // ::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: // // non-void arg: template<typename _Class, typename _ReturnType, typename _Arg1> _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); // Needed to work around TFS#287753 template<typename _Class, typename _ReturnType, typename _Arg1> _Arg1 _Arg1ClassHelperThunk(_ReturnType (__cdecl _Class::*)(_Arg1) const); template<typename _Class, typename _ReturnType, typename _Arg1> _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const); // Needed to work around TFS#287753 template<typename _Class, typename _ReturnType, typename _Arg1> _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (__cdecl _Class::*)(_Arg1) const); // void arg: template<typename _Class, typename _ReturnType> _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const); // Needed to work around TFS#287753 template<typename _Class, typename _ReturnType> _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (__cdecl _Class::*)() const); template<typename _Class, typename _ReturnType> void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const); // Needed to work around TFS#287753 template<typename _Class, typename _ReturnType> void _Arg1ClassHelperThunk(_ReturnType (__cdecl _Class::*)() const); // --- Function pointer --- template<typename _ReturnType, typename _Arg1> _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template<typename _ReturnType, typename _Arg1> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); template<typename _ReturnType>
void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); template<typename _ReturnType> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); template<typename _ReturnType, typename _Arg1> _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template<typename _ReturnType, typename _Arg1> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); template<typename _ReturnType> void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); template<typename _ReturnType> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); template<typename _ReturnType, typename _Arg1> _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template<typename _ReturnType, typename _Arg1> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); template<typename _ReturnType> void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); template<typename _ReturnType> _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); // // ASYNC TODO: See if this can be merged with _FunctionTypeTraits. // template<typename _T> struct _FunctorTypeTraits { typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; }; template<typename _T> struct _FunctorTypeTraits<_T *> { typedef decltype(_ReturnTypePFNHelperThunk(std::declval<_T*>())) _ReturnType; typedef decltype(_Arg1PFNHelperThunk(std::declval<_T*>())) _Argument1Type; }; template<typename _T> struct _ProgressTypeTraits { static const bool _TakesProgress = false;
typedef void _ProgressType; }; template<typename _T> struct _ProgressTypeTraits<progress_reporter<_T>> { static const bool _TakesProgress = true; typedef typename _T _ProgressType; }; ref class _Zip { }; // *************************************************************************** // Async Operation Task Generators // // // Functor result needs to be wrapped in a task: // template<typename _AsyncSelector, typename _ReturnType> struct _TaskGenerator { template<typename _Function> static ::Concurrency::preview::task<_ReturnType> _GenerateTask(const _Function& _Func) { return task<_ReturnType>(_Func()); } template<typename _Function, typename _ProgressObject> static ::Concurrency::preview::task<_ReturnType> _GenerateProgressAdaptedTask(const _Function& _Func, const _ProgressObject& _Progress) { return task<_ReturnType>(_Func(_Progress)); } };#if 0 template<typename _AsyncSelector> struct _TaskGenerator<_AsyncSelector, void> { template<typename _Function> static ::Concurrency::preview::task<void> _GenerateTask(const _Function& _Func) { return task<void>(_Func()); } template<typename _Function, typename _ProgressObject> static ::Concurrency::preview::task<void> _GenerateProgressAdaptedTask(const _Function& _Func, const _ProgressObject& _Progress) { return task<void>(_Func(_Progress));
} };#endif // // Functor needs to be wrapped in a task: // template<typename _ReturnType> struct _TaskGenerator<_TypeSelectorNoAsync, _ReturnType> { template<typename _Function> static ::Concurrency::preview::task<_ReturnType> _GenerateTask(const _Function& _Func) { return task<_ReturnType>(_Func); } template<typename _Function, typename _ProgressObject> static ::Concurrency::preview::task<_ReturnType> _GenerateProgressAdaptedTask(const _Function& _Func, _ProgressObject _Progress) { return task<_ReturnType>( [=]() -> _ReturnType { return _Func(_Progress); }); } }; template<> struct _TaskGenerator<_TypeSelectorNoAsync, void> { template<typename _Function> static ::Concurrency::preview::task<void> _GenerateTask(const _Function& _Func) { return task<void>(_Func); } template<typename _Function, typename _ProgressObject> static ::Concurrency::preview::task<void> _GenerateProgressAdaptedTask(const _Function& _Func, _ProgressObject _Progress) { return task<void>( [=]() { _Func(_Progress); }); } }; // // Functor returns a task: // template<typename _ReturnType> struct _TaskGenerator<_TypeSelectorAsyncTask, _ReturnType> { template<typename _Function> static ::Concurrency::preview::task<_ReturnType> _GenerateTask(const _Function& _Func)
{ return _Func(); } template<typename _Function, typename _ProgressObject> static ::Concurrency::preview::task<_ReturnType> _GenerateProgressAdaptedTask(const _Function& _Func, const _ProgressObject& _Progress) { return _Func(_Progress); } }; // *************************************************************************** // 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 WinRT interface which is being implemented. // _CompletionDelegateType : The WinRT completion delegate type for the interface. // _ProgressDelegateType : If _TakesProgress is true, the WinRT progress delegate type for the interface. If it is false, an empty WinRT type. // _ReturnType : The return type of the async construct (void for actions / non-void for operations) // // _TakesProgress : An indication as to whether or not // // _Task_Generator_Function : A function adapting the user's function into what's necessary to produce the appropriate task // // Optional: // ------------------------- // template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesProgress> struct _AsyncAttributes { }; template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits> struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, 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 _TaskGenerator<typename _TaskTraits::_AsyncKind, _ReturnType> _TaskGenerator; static const bool _TakesProgress = true; template<typename _Function, typename _ClassPtr> static ::Concurrency::preview::task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr) { return _TaskGenerator::_GenerateProgressAdaptedTask(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr)); } }; template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits> struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, false> { typedef typename ::Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename ::Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; typedef typename _ReturnType _ReturnType; typedef typename _TaskGenerator<typename _TaskTraits::_AsyncKind, _ReturnType> _TaskGenerator; static const bool _TakesProgress = false; template<typename _Function, typename _ClassPtr> static ::Concurrency::preview::task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr) { return _TaskGenerator::_GenerateTask(_Func); } }; template<typename _Function, typename _ProgressType, typename _TaskTraits> struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, 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 _TaskGenerator<typename _TaskTraits::_AsyncKind, _ReturnType> _TaskGenerator; static const bool _TakesProgress = true; template<typename _Function, typename _ClassPtr> static ::Concurrency::preview::task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr) { return _TaskGenerator::_GenerateProgressAdaptedTask(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr)); } }; template<typename _Function, typename _ProgressType, typename _TaskTraits> struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, false> { typedef typename ::Windows::Foundation::IAsyncAction _AsyncBaseType; typedef _Zip _ProgressDelegateType; typedef typename ::Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType; typedef void _ReturnType; typedef typename _TaskGenerator<typename _TaskTraits::_AsyncKind, _ReturnType> _TaskGenerator; static const bool _TakesProgress = false; template<typename _Function, typename _ClassPtr> static ::Concurrency::preview::task<_ReturnType> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr) { return _TaskGenerator::_GenerateTask(_Func); } }; template<typename _Function> struct _AsyncLambdaTypeTraits { typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType; typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _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 { public: _AsyncInfoBase() : _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), _M_id(1), _M_errorCode(S_OK), _M_completeDelegate(ref new _Attributes::_CompletionDelegateType([](_Attributes::_AsyncBaseType^){})) { } property unsigned int Id { unsigned int get() { _CheckValidStateForAsyncInfoCall(); return _M_id; } void set(unsigned int id) { _CheckValidStateForAsyncInfoCall(); if (id == 0) { throw ref new Platform::COMException(E_INVALIDARG); } _M_id = id; } } property ::Windows::Foundation::AsyncStatus Status { ::Windows::Foundation::AsyncStatus get() { _CheckValidStateForAsyncInfoCall(); _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); //
// Map our internal cancel pending to started. It's not successfully been canceled yet nor has it finished yet. It's // waiting on resolution one way or another. // switch(_Current) { case _AsyncCancelPending: _Current = _AsyncStarted; break; default: break; } return static_cast<::Windows::Foundation::AsyncStatus>(_Current); } } property ::Windows::Foundation::HResult ErrorCode { ::Windows::Foundation::HResult get() { _CheckValidStateForAsyncInfoCall(); ::Windows::Foundation::HResult _Hr; _Hr.Value = _M_errorCode; return _Hr; } } property typename _Attributes::_CompletionDelegateType^ Completed { typename _Attributes::_CompletionDelegateType^ get() { _CheckValidStateForDelegateCall(); return _M_completeDelegate; } void set(typename _Attributes::_CompletionDelegateType^ _CompleteHandler) { _CheckValidStateForDelegateCall(); _M_completeDelegate = _CompleteHandler; _M_completeDelegateContext = ::Concurrency::preview::details::_ContextCallback::_CaptureCurrent(); } } // ASYNC TODO: When the compiler can deal with virtual/overriden properties, this should just become a virtual // property Progress with overrides in the derived classes. // // ASYNC TODO: The below two should be abstract / pure virtual property typename _Attributes::_ProgressDelegateType^ Progress
{ typename typename _Attributes::_ProgressDelegateType^ get() { return _GetOnProgress(); } void set(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { _PutOnProgress(_ProgressHandler); } } virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() { throw ref new Platform::COMException(E_UNEXPECTED); } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) { throw ref new Platform::COMException(E_UNEXPECTED); } virtual typename _Attributes::_ReturnType GetResults() { throw ref new Platform::COMException(E_UNEXPECTED); } void Start() { if (_TransitionToState(_AsyncStarted)) { _OnStart(); } else { throw ref new Platform::COMException(E_ILLEGAL_STATE_CHANGE); } } void Cancel() { if (_TransitionToState(_AsyncCancelPending)) { _OnCancel(); } } void Close() { if (_TransitionToState(_AsyncClosed)) { _OnClose(); }
else { _AsyncStatusInternal current = _AsyncUndefined; _CurrentStatus(¤t); if (current != _AsyncClosed) // Closed => Closed transition is just ignored { throw ref new Platform::COMException(E_ILLEGAL_STATE_CHANGE); } } } void _FireCompletion() { _TryTransitionToCompleted(); if (_M_completeDelegate != nullptr) { // // TODO: Sort out why I cannot safely marshal a delegate with a proxy across apartments. The delegate has fields which are used in () and // are not part of the marshaled object. Using the delegate after marshaling crashes. This is TFS #265326. // typename _Attributes::_CompletionDelegateType^ _Completion = _M_completeDelegate; // ::Concurrency::preview::details::_InContext<typename _Attributes::_CompletionDelegateType^>::_Get(_M_completeDelegate, _M_completeDelegateContext); _Completion((_Attributes::_AsyncBaseType^)this); _M_completeDelegate = nullptr; } } protected: inline void _CurrentStatus(_AsyncStatusInternal *status) { InterlockedCompareExchange(reinterpret_cast<LONG*>(status), _M_currentStatus, static_cast<LONG>(*status)); } inline void _InternalErrorCode(HRESULT *error) { InterlockedCompareExchange(reinterpret_cast<LONG*>(error), _M_errorCode, static_cast<LONG>(*error)); } bool _TryTransitionToCompleted(void) { return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); }
bool _TryTransitionToCancelled(void) { return _TransitionToState(_AsyncStatusInternal::_AsyncCancelled); } bool _TryTransitionToError(const HRESULT error) { _InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(&_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 // errro hr in the case of violation. inline void _CheckValidStateForDelegateCall() { _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); if (_Current != _AsyncCreated) { throw ref new Platform::COMException(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 = _AsyncUndefined; _CurrentStatus(&_Current); if (_Current == _AsyncError) { throw ref new Platform::COMException(_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 != _AsyncCancelled && _Current != _AsyncCompleted) { throw ref new Platform::COMException(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 != _AsyncCancelled && _Current != _AsyncCompleted) { throw ref new Platform::COMException(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() { _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); return (_Current == _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(void) = 0; virtual void _OnClose(void) = 0; virtual void _OnCancel(void) = 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 = _AsyncUndefined; _CurrentStatus(&_Current); if (_Current == _AsyncClosed) { throw ref new Platform::COMException(E_ILLEGAL_METHOD_CALL); } } inline bool _TransitionToState(const _AsyncStatusInternal _NewState) { _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); // 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::_AsyncCancelled: 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<volatile LONG*>(&_M_currentStatus), _NewState, static_cast<LONG>(_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() { _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); return _IsTerminalState(_Current); } inline bool _IsTerminalState(_AsyncStatusInternal status) { return (status == _AsyncError || status == _AsyncCancelled || status == _AsyncCompleted || status == _AsyncClosed); } private: ::Concurrency::preview::details::_ContextCallback _M_completeDelegateContext; typename _Attributes::_CompletionDelegateType^ _M_completeDelegate; _AsyncStatusInternal volatile _M_currentStatus; HRESULT volatile _M_errorCode; unsigned int _M_id; }; // *************************************************************************** // 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> { public: // ASYNC TODO: When the compiler can deal with virtual/overriden properties, this should just become an override // of the base class Progress property!
virtual typename _Attributes::_ProgressDelegateType^ _GetOnProgress() override { _CheckValidStateForDelegateCall(); return _M_progressDelegate; } virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType^ _ProgressHandler) override { _CheckValidStateForDelegateCall(); _M_progressDelegate = _ProgressHandler; _M_progressDelegateContext = ::Concurrency::preview::details::_ContextCallback::_CaptureCurrent(); } public: void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue) { if (_M_progressDelegate != nullptr) { // // TODO: Sort out why I cannot safely marshal a delegate with a proxy across apartments. The delegate has fields which are used in () and // are not part of the marshaled object. Using the delegate after marshaling crashes. This is TFS #265326. // typename _Attributes::_ProgressDelegateType^ _Progress = _M_progressDelegate; // ::Concurrency::preview::details::_InContext<typename _Attributes::_ProgressDelegateType^>::_Get(_M_progressDelegate, _M_progressDelegateContext); _Progress((typename _Attributes::_AsyncBaseType^)this, _ProgressValue); } } private: ::Concurrency::preview::details::_ContextCallback _M_progressDelegateContext; typename _Attributes::_ProgressDelegateType^ _M_progressDelegate; }; template<typename _Attributes, _AsyncResultType _ResultType = SingleResult> ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> { }; // **********************************************************************
***** // Task Adaptation Layer: // // // _AsyncTaskThunkBase provides a *HOT* bridge between IAsync<Action/Operation> and task. The resultant async operation hides the hot nature // of the task (results won't be available until after Start is called, etc...). The underlying task is, however, *HOT*. // template<typename _Attributes, typename _ReturnType> ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes> { public: typedef task<_ReturnType> _TaskType; _ReturnType GetResults() { _CheckValidStateForResultsCall(); return _M_task.get(); } protected: _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) { } _AsyncTaskThunkBase() { } virtual void _OnStart() { task_continuation_context _Ct = task_continuation_context::use_current(); _M_task.then( [=](_TaskType _Antecedent) { try { _ReturnType _Val = _Antecedent.get(); } catch(task_canceled&) { _TryTransitionToCancelled(); } catch(Platform::Exception^ ex) { _TryTransitionToError(ex->HResult); } catch(...) { _TryTransitionToError(E_FAIL);
} }).then([=] (task<void>) { // // Completed happens regardless of what terminal state we transition into. // _FireCompletion(); }, _Ct); } _TaskType _M_task; }; template<typename _Attributes> ref class _AsyncTaskThunkBase<_Attributes, void> abstract : _AsyncBaseProgressLayer<_Attributes> { public: typedef task<void> _TaskType; void GetResults() { _CheckValidStateForResultsCall(); } protected: _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) { } _AsyncTaskThunkBase() { } virtual void _OnStart() { task_continuation_context _Ct = task_continuation_context::use_current(); _M_task.then( [=](_TaskType _Antecedent) { try { _Antecedent.get(); } catch(task_canceled&) { _TryTransitionToCancelled(); } catch(Platform::Exception^ ex) { _TryTransitionToError(ex->HResult); }
catch(...) { _TryTransitionToError(E_FAIL); } }).then([=] (task<void>) { // // Completed happens regardless of what terminal state we transition into. // _FireCompletion(); }, _Ct); } _TaskType _M_task; }; template<typename _Attributes> ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType> { public: _AsyncTaskThunk(const _TaskType& _Task) : _AsyncTaskThunkBase(_Task) { } protected: _AsyncTaskThunk() { } virtual void _OnClose() { } virtual void _OnCancel() { // Cancellation is not supported in PREVIEW// _M_task.cancel(); } }; // *************************************************************************** // Async Creation Layer: // // // _AsyncTaskGeneratorBase provides a truly *COLD* bridge between IAsync<Action/Operation>[WithProgress] and task. The generator does not actually // schedule a task *UNTIL* the IAsync*->Start method is called. // template<typename _Function>
ref class _AsyncTaskGeneratorThunk : _AsyncTaskThunk<typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes> { public: typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; typedef typename _AsyncTaskThunk<_Attributes> _Base; typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; _AsyncTaskGeneratorThunk(const _Function& _Func) : _M_func(_Func) { } 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() { // // 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); _Base::_OnStart(); } virtual void _OnCancel() { _AsyncStatusInternal _Current = _AsyncUndefined; _CurrentStatus(&_Current); // TODO: What's the semantic on Cancel before Start. The spec and AsyncBase implementation contradict each other. _Base::_OnCancel(); } private: _Function _M_func; };}; /// <summary>/// Creates a WinRT asynchronous construct based on a user supplied lambda (or functor). The return type of create_async is one of either/// IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperatio
n<TResult>, or IAsyncOperationWithProgress<TResult, TProgress> based on the/// signature of the lambda passed to the method. /// </summary>/// <param name="_Func">/// The user supplied lambda from which to create a WinRT asynchronous construct. The body of the lambda is the asynchronous action or operation /// which will be executed within the MTA. The return type of the lambda determines whether the construct is an action or an operation. ////// Void returning lambdas cause the creation of actions. Non-void returning lambdas cause the creation of/// operations of TResult where TResult is the return type of the lambda.////// The lambda may take either zero or one argument. A zero argument lambda creates an asynchronous construct without the capability for progress/// reporting. A one argument lambda must take an argument of the form progress_reporter<TProgress>. Lambdas that take an argument of this form/// construct an asynchronous construct which reports progress of type TProgress each time the report method of the progress_reporter object is/// called./// </param>/// <returns>/// 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./// </returns>template<typename _Function>typename details::_AsyncTaskGeneratorThunk<_Function>::_AsyncBaseType^ create_async(const _Function& _Func){ return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func);} #endif /// <summary>/// Returns an indication of whether the task which is currently executing inline on the current context/// is in the midst of an active cancellation (or will be shortly). /// </summary>/// <returns>/// <c>true</c> if the task which is currently executing is canceling, <c>false</c> otherwise./// </returns>/**/inline bool is_current_task_canceling(){ return is_current_task_group_canceling();} /// <summary>/// Cancel the currently running task. This function can be called from within a task's body of/// execution to begin a cancellation.
/// </summary>/**/__declspec(noreturn)inline void __cdecl cancel_current_task(){ throw task_canceled();} namespace details { // Helper struct for when_all operators to know when tasks have completed template<typename _Type> struct _RunAllParam { _RunAllParam() : _M_lCompleteCount(0), _M_lCancelCount(0), _M_numTasks(0) { } void _Resize(size_t _Len, bool _SkipVector = false) { _M_numTasks = _Len; if (!_SkipVector) _M_vector.resize(_Len); _M_contexts.resize(_Len); } std::vector<_Type> _M_vector; std::vector<_ContextCallback> _M_contexts; _Type _M_mergeVal; long volatile _M_lCompleteCount; long volatile _M_lCancelCount; size_t _M_numTasks; }; // Helper struct specialization for void template<> struct _RunAllParam<void> { _RunAllParam() : _M_lCompleteCount(0), _M_lCancelCount(0), _M_numTasks(0) { } void _Resize(size_t _Len) { _M_numTasks = _Len; } long volatile _M_lCompleteCount; long volatile _M_lCancelCount; size_t _M_numTasks; }; template<typename _ElementType, typename _Function, typename _TaskType> void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, con
st task_completion_event<_Unit_type>& _Completed, _Function _Func, task<_TaskType>& _Task) { if (_Task._GetImpl()->_IsCompleted()) { _Func(); if (_InterlockedIncrement(&_PParam->_M_lCompleteCount) == ((long) _PParam->_M_numTasks)) { if (!_Completed.set(_Unit_type())) { delete _PParam; } } } else { _ASSERT(_Task._GetImpl()->_IsCanceled()); if (_Task._GetImpl()->_HasUserException()) { // _Cancel will return false if the TCE is already canceled with or without exception if (!_Completed._Cancel(_Task._GetImpl()->_GetExceptionHolder())) { // If the TCE has already reached a terminal state we must observe this exception. _Task._GetImpl()->_ObserveUserException(); } } else { _Completed._Cancel(); } if (_InterlockedIncrement(&_PParam->_M_lCompleteCount) == ((long) _PParam->_M_numTasks)) { delete _PParam; } } } template<typename _ElementType, typename _Iterator> struct _WhenAllImpl { static task<std::vector<_ElementType>> _Perform(_Iterator _Begin, _Iterator _End) { auto _PParam = new _RunAllParam<_ElementType>(); task_completion_event<_Unit_type> _Completed; task<_Unit_type> _All_tasks_completed(_Completed); bool _HasAsync = false; if( _Begin == _End )
{ _Completed.set(_Unit_type()); } else { // Copy the tasks to an internal vector for processing. This allows const iterator types // to be processed. std::vector<task<_ElementType>> _Tasks(_Begin, _End); _PParam->_Resize(_Tasks.size()); size_t _Index = 0; for (auto _PTask = _Tasks.begin(); _PTask != _Tasks.end(); ++_PTask) { if (_PTask->_IsAsync()) { _HasAsync = true; } _PTask->_Then([_PParam, _Index, _Completed](task<_ElementType> _ResultTask) { auto _Func = [_PParam, _Index, &_ResultTask](){ _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); _PParam->_M_contexts[_Index] = _ResultContext<_ElementType>::_GetContext(false); }; _WhenAllContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); _Index++; } } if (_HasAsync) { _All_tasks_completed._SetAsync(); } return _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { auto _Result = _PParam->_M_vector; // copy by value size_t _Index = 0; for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) { *_It = ::Concurrency::preview::details::_ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_Index++], false); } delete _PParam;
return _Result; }, false, true); } }; template<typename _ElementType, typename _Iterator> struct _WhenAllImpl<std::vector<_ElementType>, _Iterator> { static task<std::vector<_ElementType>> _Perform(_Iterator _Begin, _Iterator _End) { auto _PParam = new _RunAllParam<std::vector<_ElementType>>(); task_completion_event<_Unit_type> _Completed; task<_Unit_type> _All_tasks_completed(_Completed); bool _HasAsync = false; if( _Begin == _End ) { _Completed.set(_Unit_type()); } else { // Copy the tasks to an internal vector for processing. This allows const iterator types // to be processed. std::vector<task<std::vector<_ElementType>>> _Tasks(_Begin, _End); _PParam->_Resize(_Tasks.size()); size_t _Index = 0; for (auto _PTask = _Tasks.begin(); _PTask != _Tasks.end(); ++_PTask) { if (_PTask->_IsAsync()) { _HasAsync = true; } _PTask->_Then([_PParam, _Index, _Completed](task<std::vector<_ElementType>> _ResultTask) { auto _Func = [_PParam, _Index, &_ResultTask]() { _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); _PParam->_M_contexts[_Index] = ::Concurrency::preview::details::_ResultContext<_ElementType>::_GetContext(false); }; _WhenAllContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); _Index++; } }
if (_HasAsync) { _All_tasks_completed._SetAsync(); } return _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ElementType> { _ASSERT(_PParam->_M_lCompleteCount == ((long) _PParam->_M_numTasks )); std::vector<_ElementType> _Result; for(unsigned int _I = 0; _I < _PParam->_M_numTasks; _I++) { std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I]; for (auto _It = _Vec.begin(); _It != _Vec.end(); ++_It) { *_It = ::Concurrency::preview::details::_ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_I], false); } _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); } delete _PParam; return _Result; }, false, true); } }; template<typename _Iterator> struct _WhenAllImpl<void, _Iterator> { static task<void> _Perform(_Iterator _Begin, _Iterator _End) { auto _PParam = new _RunAllParam<_Unit_type>(); task_completion_event<_Unit_type> _Completed; task<_Unit_type> _All_tasks_completed(_Completed); bool _HasAsync = false; if( _Begin == _End ) { _Completed.set(_Unit_type()); } else { // Copy the tasks to an internal vector for processing. This allows const iterator types // to be processed. std::vector<task<void>> _Tasks(_Begin, _End); _PParam->_Resize(_Tasks.size()); for (auto _PTask = _Tasks.begin(); _PTask != _Tasks.end(); ++_PTask) {
if (_PTask->_IsAsync()) { _HasAsync = true; } _PTask->_Then([_PParam, _Completed](task<void> _ResultTask) { auto _Func = [](){}; _WhenAllContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); } } if (_HasAsync) { _All_tasks_completed._SetAsync(); } return _All_tasks_completed.then([=](_Unit_type){ delete _PParam; }); } }; template<typename _ReturnType> task<std::vector<_ReturnType>> _WhenAllVectorAndValue(task<std::vector<_ReturnType>>& _VectorTask, task<_ReturnType>& _ValueTask, bool _OutputVectorFirst) { auto _PParam = new _RunAllParam<_ReturnType>(); task_completion_event<_Unit_type> _Completed; task<_Unit_type> _All_tasks_completed(_Completed); _PParam->_Resize(2, true); _VectorTask._Then([_PParam, _Completed](task<std::vector<_ReturnType>> _ResultTask) { auto _Func = [_PParam, &_ResultTask]() { _PParam->_M_vector = _ResultTask._GetImpl()->_GetResult(); _PParam->_M_contexts[0] = _ResultContext<_ReturnType>::_GetContext(false); }; _WhenAllContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); _ValueTask._Then([_PParam, _Completed](task<_ReturnType> _ResultTask) { auto _Func = [_PParam, &_ResultTask]() {
_PParam->_M_mergeVal = _ResultTask._GetImpl()->_GetResult(); _PParam->_M_contexts[1] = _ResultContext<_ReturnType>::_GetContext(false); }; _WhenAllContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); if (_VectorTask._IsAsync() || _ValueTask._IsAsync()) { _All_tasks_completed._SetAsync(); } return _All_tasks_completed._Then([=](_Unit_type) -> std::vector<_ReturnType> { _ASSERT(_PParam->_M_lCompleteCount == ((long) 2)); auto _Result = _PParam->_M_vector; // copy by value for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) { *_It = ::Concurrency::preview::details::_ResultContext<_ReturnType>::_GetValue(*_It, _PParam->_M_contexts[0], false); } if (_OutputVectorFirst == true) { _Result.push_back(::Concurrency::preview::details::_ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); } else { _Result.insert(_Result.begin(), ::Concurrency::preview::details::_ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); } delete _PParam; return _Result; }, false, true); }} // namespace details /// <summary>/// First-class tasks when_all API using begin and end iterators/// </summary>/// <typeparam name="_Iterator">/// The type of the input iterator./// </typeparam>/// <param name="_Begin">/// Position of the first element in the range of elements to be combined and waited on./// </param>/// <param name="_End">
/// Position of the first element beyond the range of elements to be combined and waited on./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template <typename _Iterator>auto when_all(_Iterator _Begin, _Iterator _End) -> decltype (details::_WhenAllImpl<std::iterator_traits<_Iterator>::value_type::_TaskType, _Iterator>::_Perform(_Begin, _End)){ typedef std::iterator_traits<_Iterator>::value_type::_TaskType _ElementType; return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_Begin, _End);} /// <summary>/// When_all operator (task1 && task2) for two tasks/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator&&(task<_ReturnType> _Lhs, task<_ReturnType> _Rhs){ task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2);} /// <summary>/// When_all operator (task1 && task2) for two tasks/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks.
/// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator&&(task<std::vector<_ReturnType>> _Lhs, task<_ReturnType> _Rhs){ return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true);} /// <summary>/// When_all operator (task1 && task2) for two tasks/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator&&(task<_ReturnType> _Lhs, task<std::vector<_ReturnType>> _Rhs){ return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false);} /// <summary>/// When_all operator (task1 && task2) for two tasks/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>
/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator&&(task<std::vector<_ReturnType>> _Lhs, task<std::vector<_ReturnType>> _Rhs){ task<std::vector<_ReturnType>> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2);} /// <summary>/// When_all operator (task1 && task2) for two tasks/// </summary>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/inline task<void> operator&&(task<void> _Lhs, task<void> _Rhs){ task<void> _PTasks[2] = {_Lhs, _Rhs}; return when_all(_PTasks, _PTasks+2);} namespace details { // Helper struct for when_any operators to know when tasks have completed struct _RunAnyParam { _RunAnyParam() : _M_lCompleteCount(0), _M_lCancelCount(0), _M_numTasks(0) { } long volatile _M_lCompleteCount;
long volatile _M_lCancelCount; size_t _M_numTasks; }; template<typename _CompletionType, typename _Function, typename _TaskType> void _WhenAnyContinuationWrapper(_RunAnyParam* _PParam, const task_completion_event<_CompletionType>& _Completed, _Function _Func, task<_TaskType>& _Task) { if (_Task._GetImpl()->_IsCompleted()) { _Func(); if (_InterlockedIncrement(&_PParam->_M_lCompleteCount) == ((long) _PParam->_M_numTasks)) { delete _PParam; } } else { _ASSERT(_Task._GetImpl()->_IsCanceled()); if (_Task._GetImpl()->_HasUserException()) { _Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder()); } if (_InterlockedIncrement(&_PParam->_M_lCancelCount) == ((long) _PParam->_M_numTasks)) { _Completed._Cancel(); } if (_InterlockedIncrement(&_PParam->_M_lCompleteCount) == ((long) _PParam->_M_numTasks)) { delete _PParam; } } } template<typename _ElementType, typename _Iterator> struct _WhenAnyImpl { static task<std::pair<_ElementType, size_t>> _Perform(_Iterator _Begin, _Iterator _End) { if( _Begin == _End ) { throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } auto _PParam = new _RunAnyParam(); task_completion_event<std::pair<_ElementType, size_t>> _Completed;
task<std::pair<_ElementType, size_t>> _Any_tasks_completed(_Completed); _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; bool _HasAsync = false; // Copy the tasks to an internal vector for processing. This allows const iterator types // to be processed. std::vector<task<_ElementType>> _Tasks(_Begin, _End); _PParam->_M_numTasks = _Tasks.size(); size_t index = 0; for (auto _PTask = _Tasks.begin(); _PTask != _Tasks.end(); ++_PTask) { if (_PTask->_IsAsync()) { _HasAsync = true; } _PTask->_Then([_PParam, _Completed, index](task<_ElementType> _ResultTask) { auto _Func = [&_ResultTask, &_Completed, index]() { _ElementType _Result = _ResultTask._GetImpl()->_GetResult(); _Completed.set(std::make_pair(_Result, index)); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); index++; } if (_HasAsync) { _Any_tasks_completed._SetAsync(); } return _Any_tasks_completed._Then([=](std::pair<_ElementType, size_t> _Result) -> std::pair<_ElementType, size_t> { return _Result; }, false, true); } }; template<typename _Iterator> struct _WhenAnyImpl<void, _Iterator> { static task<size_t> _Perform(_Iterator _Begin, _Iterator _End) { if( _Begin == _End )
{ throw invalid_operation("when_any(begin, end) cannot be called on an empty container."); } auto _PParam = new _RunAnyParam(); task_completion_event<size_t> _Completed; task<size_t> _Any_tasks_completed(_Completed); bool _HasAsync = false; // Copy the tasks to an internal vector for processing. This allows const iterator types // to be processed. std::vector<task<void>> _Tasks(_Begin, _End); _PParam->_M_numTasks = _Tasks.size(); size_t index = 0; for (auto _PTask = _Tasks.begin(); _PTask != _Tasks.end(); ++_PTask) { if (_PTask->_IsAsync()) { _HasAsync = true; } _PTask->_Then([_PParam, _Completed, index](task<void> _ResultTask) { auto _Func = [&_ResultTask, &_Completed, index]() { _Completed.set(index); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); index++; } if (_HasAsync) { _Any_tasks_completed._SetAsync(); } return _Any_tasks_completed.then([=](size_t _Result) -> size_t { return _Result; }); } }; #if defined(__cplusplus_winrt) template<typename _T> task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T>^ op) { return task<_T>(op); }
template<typename _T, typename _Progress> task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>^ op) { return task<_T>(op); } inline task<void> _To_task_helper(Windows::Foundation::IAsyncAction^ op) { return task<void>(op); } template<typename _Progress> task<void> _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress>^ op) { return task<void>(op); }#endif } // namespace details /// <summary>/// First-class tasks when_any API using begin and end iterators/// </summary>/// <typeparam name="_Iterator">/// The type of the input iterator./// </typeparam>/// <param name="_Begin">/// Position of the first element in the range of elements to be combined and waited on./// </param>/// <param name="_End">/// Position of the first element beyond the range of elements to be combined and waited on./// </param>/// <returns>/// A task that completes when any one of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<std::pair<T, size_t>></c>. 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 <c>void</c> /// the output is a <c>task<size_t></c>, where the result is the index of the completing task./// </returns>/**/template<typename _Iterator>auto when_any(_Iterator _Begin, _Iterator _End) -> decltype (details::_WhenAnyImpl<std::iterator_traits<_Iterator>::value_type::_TaskType, _Iterator>::_Perform(_Begin, _End)){ typedef std::iterator_traits<_Iterator>::value_type::_TaskType _ElementType;
return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_Begin, _End);} /// <summary>/// When_any operator (task1 || task2) for two task<T>/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<T></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<_ReturnType> operator||(task<_ReturnType> _Lhs, task<_ReturnType> _Rhs){ auto _PParam = new details::_RunAnyParam(); task_completion_event<_ReturnType> _Completed; task<_ReturnType> _Any_tasks_completed(_Completed); _PParam->_M_numTasks = 2; auto _Continuation = [_PParam, _Completed](task<_ReturnType> _ResultTask) { auto _Func = [&_ResultTask, &_Completed]() { _ReturnType _Result = _ResultTask._GetImpl()->_GetResult(); _Completed.set(_Result); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }; _Lhs._Then(_Continuation, false, false); _Rhs._Then(_Continuation, false, false); if (_Lhs._IsAsync() || _Rhs._IsAsync()) { _Any_tasks_completed._SetAsync(); } return _Any_tasks_completed.then([=](_ReturnType _Ret) -> _ReturnType { return _Ret; });} /// <summary>
/// When_any operator (task1 || task2) for one task<std::vector<T>> and a task<T>/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<T></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator||(task<std::vector<_ReturnType>> _Lhs, task<_ReturnType> _Rhs){ auto _PParam = new details::_RunAnyParam(); task_completion_event<std::vector<_ReturnType>> _Completed; task<std::vector<_ReturnType>> _Any_tasks_completed(_Completed); _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; _PParam->_M_numTasks = 2; _Lhs._Then([_PParam, _Completed](task<std::vector<_ReturnType>> _ResultTask) { auto _Func = [&_ResultTask, &_Completed]() { std::vector<_ReturnType> _Result = _ResultTask._GetImpl()->_GetResult(); _Completed.set(_Result); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); _Rhs._Then([_PParam, _Completed](task<_ReturnType> _ResultTask) { auto _Func = [&_ResultTask, &_Completed]() { _ReturnType _Result = _ResultTask._GetImpl()->_GetResult(); std::vector<_ReturnType> _Vec; _Vec.push_back(_Result); _Completed.set(_Vec); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }, false, false); if (_Lhs._IsAsync() || _Rhs._IsAsync()) { _Any_tasks_completed._SetAsync();
} return _Any_tasks_completed._Then([=](std::vector<_ReturnType> _Ret) -> std::vector<_ReturnType> { return _Ret; }, false, true);} /// <summary>/// When_any operator (task1 || task2) for one task<T> and a task<std::vector<T>>/// </summary>/// <typeparam name="_ReturnType">/// The type of the tasks./// </typeparam>/// <param name="_Lhs">/// The first task to combine into this when_all task./// </param>/// <param name="_Rhs">/// The second task to combine into this when_all task./// </param>/// <returns>/// A task that completes when all of the input tasks are complete. If the input tasks are of type <c>T</c>, the output/// of this function will be a <c>task<T></c>. If the input tasks are of type <c>void</c> the output will /// also be a <c>task<void></c>./// </returns>/**/template<typename _ReturnType>task<std::vector<_ReturnType>> operator||(task<_ReturnType> _Lhs, task<std::vector<_ReturnType>> _Rhs){ return _Rhs || _Lhs;} // When_any operator (task1 || task2) for two task<void>inline task<void> operator||(task<void> _Lhs, task<void> _Rhs){ auto _PParam = new details::_RunAnyParam(); task_completion_event<details::_Unit_type> _Completed; task<details::_Unit_type> _Any_task_completed(_Completed); _PParam->_M_numTasks = 2; auto _Continuation = [_PParam, _Completed](task<void> _ResultTask) { auto _Func = [&_Completed]() { _Completed.set(details::_Unit_type()); }; _WhenAnyContinuationWrapper(_PParam, _Completed, _Func, _ResultTask); }; _Lhs._Then(_Continuation, false, false); _Rhs._Then(_Continuation, false, false);
if (_Lhs._IsAsync() || _Rhs._IsAsync()) { _Any_task_completed._SetAsync(); } return _Any_task_completed.then([=](details::_Unit_type){ });}} // namespace preview} // namespace Concurrency namespace concurrency = Concurrency; #pragma warning(pop) #pragma pack(pop) #endif // _PPLTASKS_H