1 /***
2 * Copyright (C) Microsoft. All rights reserved.
3 * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4 *
5 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6 *
7 * Parallel Patterns Library - PPLx Tasks
8 *
9 * For the latest on this and related APIs, please see: https://github.com/Microsoft/cpprestsdk
10 *
11 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
12 ****/
13
14 #pragma once
15
16 #ifndef PPLXTASKS_H
17 #define PPLXTASKS_H
18
19 #include "cpprest/details/cpprest_compat.h"
20
21 #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) && !CPPREST_FORCE_PPLX
22 #include <ppltasks.h>
23 namespace pplx = Concurrency;
24
25 namespace Concurrency
26 {
27 /// <summary>
28 /// Sets the ambient scheduler to be used by the PPL constructs.
29 /// </summary>
30 _ASYNCRTIMP void __cdecl set_cpprestsdk_ambient_scheduler(const std::shared_ptr<scheduler_interface>& _Scheduler);
31
32 /// <summary>
33 /// Gets the ambient scheduler to be used by the PPL constructs
34 /// </summary>
35 _ASYNCRTIMP const std::shared_ptr<scheduler_interface>& __cdecl get_cpprestsdk_ambient_scheduler();
36
37 } // namespace Concurrency
38
39 #if (_MSC_VER >= 1900)
40 #include <concrt.h>
41 namespace Concurrency
42 {
43 namespace extensibility
44 {
45 typedef ::std::condition_variable condition_variable_t;
46 typedef ::std::mutex critical_section_t;
47 typedef ::std::unique_lock<::std::mutex> scoped_critical_section_t;
48
49 typedef ::Concurrency::event event_t;
50 typedef ::Concurrency::reader_writer_lock reader_writer_lock_t;
51 typedef ::Concurrency::reader_writer_lock::scoped_lock scoped_rw_lock_t;
52 typedef ::Concurrency::reader_writer_lock::scoped_lock_read scoped_read_lock_t;
53
54 typedef ::Concurrency::details::_ReentrantBlockingLock recursive_lock_t;
55 typedef recursive_lock_t::_Scoped_lock scoped_recursive_lock_t;
56 } // namespace extensibility
57 } // namespace Concurrency
58 #endif // _MSC_VER >= 1900
59 #else
60
61 #include "pplx/pplx.h"
62
63 #if defined(__ANDROID__)
64 #include <jni.h>
65 void cpprest_init(JavaVM*);
66 #endif
67
68 // Cannot build using a compiler that is older than dev10 SP1
69 #if defined(_MSC_VER)
70 #if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/
71 #error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks
72 #endif /*IFSTRIP=IGN*/
73 #endif /* defined(_MSC_VER) */
74
75 #include <algorithm>
76 #include <atomic>
77 #include <exception>
78 #include <functional>
79 #include <utility>
80 #include <vector>
81
82 #if defined(_MSC_VER)
83 #include <intrin.h>
84 #if defined(__cplusplus_winrt)
85 #include <agile.h>
86 #include <ctxtcall.h>
87 #include <winapifamily.h>
88
89 #include <windows.h>
90 #ifndef _UITHREADCTXT_SUPPORT
91
92 #ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/
93
94 // It is safe to include winapifamily as WINAPI_FAMILY was defined by the user
95 #include <winapifamily.h>
96
97 #if WINAPI_FAMILY == WINAPI_FAMILY_APP
98 // UI thread context support is not required for desktop and Windows Store apps
99 #define _UITHREADCTXT_SUPPORT 0
100 #elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP
101 // UI thread context support is not required for desktop and Windows Store apps
102 #define _UITHREADCTXT_SUPPORT 0
103 #else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */
104 #define _UITHREADCTXT_SUPPORT 1
105 #endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */
106
107 #else /* WINAPI_FAMILY */
108 // Not supported without a WINAPI_FAMILY setting.
109 #define _UITHREADCTXT_SUPPORT 0
110 #endif /* WINAPI_FAMILY */
111
112 #endif /* _UITHREADCTXT_SUPPORT */
113
114 #if _UITHREADCTXT_SUPPORT
115 #include <uithreadctxt.h>
116 #endif /* _UITHREADCTXT_SUPPORT */
117
118 #pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "1")
119 #else /* defined(__cplusplus_winrt) */
120 #pragma detect_mismatch("PPLXTASKS_WITH_WINRT", "0")
121 #endif /* defined(__cplusplus_winrt) */
122 #endif /* defined(_MSC_VER) */
123
124 #ifdef _DEBUG
125 #define _DBG_ONLY(X) X
126 #else
127 #define _DBG_ONLY(X)
128 #endif // #ifdef _DEBUG
129
130 // std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11.
131 #ifdef _MSC_VER
132 #if _MSC_VER < 1700 /*IFSTRIP=IGN*/
133 namespace std
134 {
135 template<class _E>
make_exception_ptr(_E _Except)136 exception_ptr make_exception_ptr(_E _Except)
137 {
138 return copy_exception(_Except);
139 }
140 } // namespace std
141 #endif /* _MSC_VER < 1700 */
142 #ifndef PPLX_TASK_ASYNC_LOGGING
143 #if _MSC_VER >= 1800 && defined(__cplusplus_winrt)
144 #define PPLX_TASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt
145 #else
146 #define PPLX_TASK_ASYNC_LOGGING 0
147 #endif
148 #endif /* !PPLX_TASK_ASYNC_LOGGING */
149 #endif /* _MSC_VER */
150
151 #pragma pack(push, _CRT_PACKING)
152
153 #if defined(_MSC_VER)
154 #pragma warning(push)
155 #pragma warning(disable : 28197)
156 #pragma warning(disable : 4100) // Unreferenced formal parameter - needed for document generation
157 #pragma warning(disable : 4127) // constant express in if condition - we use it for meta programming
158 #endif /* defined(_MSC_VER) */
159
160 // All CRT public header files are required to be protected from the macro new
161 #pragma push_macro("new")
162 #undef new
163
164 // stuff ported from Dev11 CRT
165 // NOTE: this doesn't actually match std::declval. it behaves differently for void!
166 // so don't blindly change it to std::declval.
167 namespace stdx
168 {
169 template<class _T>
170 _T&& declval();
171 }
172
173 /// <summary>
174 /// The <c>pplx</c> namespace provides classes and functions that give you access to the Concurrency Runtime,
175 /// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.
176 /// </summary>
177 /**/
178 namespace pplx
179 {
180 /// <summary>
181 /// A type that represents the terminal state of a task. Valid values are <c>completed</c> and <c>canceled</c>.
182 /// </summary>
183 /// <seealso cref="task Class"/>
184 /**/
185 typedef task_group_status task_status;
186
187 template<typename _Type>
188 class task;
189 template<>
190 class task<void>;
191
192 // In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds,
193 // default to only one frame.
194 #ifndef PPLX_TASK_SAVE_FRAME_COUNT
195 #ifdef _DEBUG
196 #define PPLX_TASK_SAVE_FRAME_COUNT 10
197 #else
198 #define PPLX_TASK_SAVE_FRAME_COUNT 1
199 #endif
200 #endif
201
202 /// <summary>
203 /// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified,
204 /// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be
205 /// captured.
206 /// </summary>
207 /// <ramarks>
208 /// This needs to be defined as a macro rather than a function so that if we're only gathering one frame,
209 /// _ReturnAddress() will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack,
210 /// itself.
211 /// </remarks>
212 #if PPLX_TASK_SAVE_FRAME_COUNT > 1
213 #if defined(__cplusplus_winrt) && !defined(_DEBUG)
214 #pragma message( \
215 "WARNING: Redefining PPLX_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!")
216 #define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress())
217 #else
218 #define PPLX_CAPTURE_CALLSTACK() \
219 ::pplx::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPLX_TASK_SAVE_FRAME_COUNT)
220 #endif
221 #else
222 #define PPLX_CAPTURE_CALLSTACK() ::pplx::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress())
223 #endif
224
225 /// <summary>
226 /// Returns an indication of whether the task that is currently executing has received a request to cancel its
227 /// execution. Cancellation is requested on a task if the task was created with a cancellation token, and
228 /// the token source associated with that token is canceled.
229 /// </summary>
230 /// <returns>
231 /// <c>true</c> if the currently executing task has received a request for cancellation, <c>false</c> otherwise.
232 /// </returns>
233 /// <remarks>
234 /// If you call this method in the body of a task and it returns <c>true</c>, you must respond with a call to
235 /// <see cref="cancel_current_task Function">cancel_current_task</see> to acknowledge the cancellation request,
236 /// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into
237 /// the <c>canceled</c> state. If you do not respond and continue execution, or return instead of calling
238 /// <c>cancel_current_task</c>, the task will enter the <c>completed</c> state when it is done.
239 /// state.
240 /// <para>A task is not cancelable if it was created without a cancellation token.</para>
241 /// </remarks>
242 /// <seealso cref="task Class"/>
243 /// <seealso cref="cancellation_token_source Class"/>
244 /// <seealso cref="cancellation_token Class"/>
245 /// <seealso cref="cancel_current_task Function"/>
246 /**/
is_task_cancellation_requested()247 inline bool _pplx_cdecl is_task_cancellation_requested()
248 {
249 return ::pplx::details::_TaskCollection_t::_Is_cancellation_requested();
250 }
251
252 /// <summary>
253 /// Cancels the currently executing task. This function can be called from within the body of a task to abort the
254 /// task's execution and cause it to enter the <c>canceled</c> state. While it may be used in response to
255 /// the <see cref="is_task_cancellation_requested Function">is_task_cancellation_requested</see> function, you may
256 /// also use it by itself, to initiate cancellation of the task that is currently executing.
257 /// <para>It is not a supported scenario to call this function if you are not within the body of a <c>task</c>.
258 /// Doing so will result in undefined behavior such as a crash or a hang in your application.</para>
259 /// </summary>
260 /// <seealso cref="task Class"/>
261 /**/
cancel_current_task()262 inline __declspec(noreturn) void _pplx_cdecl cancel_current_task() { throw task_canceled(); }
263
264 namespace details
265 {
266 /// <summary>
267 /// Callstack container, which is used to capture and preserve callstacks in ppltasks.
268 /// Members of this class is examined by vc debugger, thus there will be no public access methods.
269 /// Please note that names of this class should be kept stable for debugger examining.
270 /// </summary>
271 class _TaskCreationCallstack
272 {
273 private:
274 // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame;
275 // otherwise, _M_Frame will store all the callstack frames.
276 void* _M_SingleFrame;
277 std::vector<void*> _M_frames;
278
279 public:
_TaskCreationCallstack()280 _TaskCreationCallstack() { _M_SingleFrame = nullptr; }
281
282 // Store one frame of callstack. This function works for both Debug / Release CRT.
_CaptureSingleFrameCallstack(void * _SingleFrame)283 static _TaskCreationCallstack _CaptureSingleFrameCallstack(void* _SingleFrame)
284 {
285 _TaskCreationCallstack _csc;
286 _csc._M_SingleFrame = _SingleFrame;
287 return _csc;
288 }
289
290 // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT.
_CaptureMultiFramesCallstack(size_t _CaptureFrames)291 __declspec(noinline) static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames)
292 {
293 _TaskCreationCallstack _csc;
294 _csc._M_frames.resize(_CaptureFrames);
295 // skip 2 frames to make sure callstack starts from user code
296 _csc._M_frames.resize(::pplx::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames));
297 return _csc;
298 }
299 };
300 typedef unsigned char _Unit_type;
301
302 struct _TypeSelectorNoAsync
303 {
304 };
305 struct _TypeSelectorAsyncOperationOrTask
306 {
307 };
308 struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask
309 {
310 };
311 struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask
312 {
313 };
314 struct _TypeSelectorAsyncAction
315 {
316 };
317 struct _TypeSelectorAsyncActionWithProgress
318 {
319 };
320 struct _TypeSelectorAsyncOperationWithProgress
321 {
322 };
323
324 template<typename _Ty>
325 struct _NormalizeVoidToUnitType
326 {
327 typedef _Ty _Type;
328 };
329
330 template<>
331 struct _NormalizeVoidToUnitType<void>
332 {
333 typedef _Unit_type _Type;
334 };
335
336 template<typename _T>
337 struct _IsUnwrappedAsyncSelector
338 {
339 static const bool _Value = true;
340 };
341
342 template<>
343 struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync>
344 {
345 static const bool _Value = false;
346 };
347
348 template<typename _Ty>
349 struct _UnwrapTaskType
350 {
351 typedef _Ty _Type;
352 };
353
354 template<typename _Ty>
355 struct _UnwrapTaskType<task<_Ty>>
356 {
357 typedef _Ty _Type;
358 };
359
360 template<typename _T>
361 _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>);
362
363 _TypeSelectorNoAsync _AsyncOperationKindSelector(...);
364
365 #if defined(__cplusplus_winrt)
366 template<typename _Type>
367 struct _Unhat
368 {
369 typedef _Type _Value;
370 };
371
372 template<typename _Type>
373 struct _Unhat<_Type ^>
374 {
375 typedef _Type _Value;
376 };
377
378 value struct _NonUserType
379 {
380 public:
381 int _Dummy;
382 };
383
384 template<typename _Type, bool _IsValueTypeOrRefType = __is_valid_winrt_type(_Type)>
385 struct _ValueTypeOrRefType
386 {
387 typedef _NonUserType _Value;
388 };
389
390 template<typename _Type>
391 struct _ValueTypeOrRefType<_Type, true>
392 {
393 typedef _Type _Value;
394 };
395
396 template<typename _T1, typename _T2>
397 _T2 _ProgressTypeSelector(Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2> ^);
398
399 template<typename _T1>
400 _T1 _ProgressTypeSelector(Windows::Foundation::IAsyncActionWithProgress<_T1> ^);
401
402 template<typename _Type>
403 struct _GetProgressType
404 {
405 typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value;
406 };
407
408 template<typename _Type>
409 struct _IsIAsyncInfo
410 {
411 static const bool _Value = __is_base_of(Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_Value);
412 };
413
414 template<typename _T>
415 _TypeSelectorAsyncOperation _AsyncOperationKindSelector(Windows::Foundation::IAsyncOperation<_T> ^);
416
417 _TypeSelectorAsyncAction _AsyncOperationKindSelector(Windows::Foundation::IAsyncAction ^);
418
419 template<typename _T1, typename _T2>
420 _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(
421 Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2> ^);
422
423 template<typename _T>
424 _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(Windows::Foundation::IAsyncActionWithProgress<_T> ^);
425
426 template<typename _Type, bool _IsAsync = _IsIAsyncInfo<_Type>::_Value>
427 struct _TaskTypeTraits
428 {
429 typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType;
430 typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind;
431 typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType;
432
433 static const bool _IsAsyncTask = _IsAsync;
434 static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;
435 };
436
437 template<typename _Type>
438 struct _TaskTypeTraits<_Type, true>
439 {
440 typedef decltype(((_Type) nullptr)->GetResults()) _TaskRetType;
441 typedef _TaskRetType _NormalizedTaskRetType;
442 typedef decltype(_AsyncOperationKindSelector((_Type) nullptr)) _AsyncKind;
443
444 static const bool _IsAsyncTask = true;
445 static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;
446 };
447
448 #else /* defined (__cplusplus_winrt) */
449 template<typename _Type>
450 struct _IsIAsyncInfo
451 {
452 static const bool _Value = false;
453 };
454
455 template<typename _Type, bool _IsAsync = false>
456 struct _TaskTypeTraits
457 {
458 typedef typename _UnwrapTaskType<_Type>::_Type _TaskRetType;
459 typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind;
460 typedef typename _NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType;
461
462 static const bool _IsAsyncTask = false;
463 static const bool _IsUnwrappedTaskOrAsync = _IsUnwrappedAsyncSelector<_AsyncKind>::_Value;
464 };
465 #endif /* defined (__cplusplus_winrt) */
466
467 template<typename _Function>
468 auto _IsCallable(_Function _Func, int) -> decltype(_Func(), std::true_type())
469 {
470 (void)(_Func);
471 return std::true_type();
472 }
473 template<typename _Function>
474 std::false_type _IsCallable(_Function, ...)
475 {
476 return std::false_type();
477 }
478
479 template<>
480 struct _TaskTypeTraits<void>
481 {
482 typedef void _TaskRetType;
483 typedef _TypeSelectorNoAsync _AsyncKind;
484 typedef _Unit_type _NormalizedTaskRetType;
485
486 static const bool _IsAsyncTask = false;
487 static const bool _IsUnwrappedTaskOrAsync = false;
488 };
489
490 template<typename _Type>
491 task<_Type> _To_task(_Type t);
492
493 template<typename _Func>
494 task<void> _To_task_void(_Func f);
495
496 struct _BadContinuationParamType
497 {
498 };
499
500 template<typename _Function, typename _Type>
501 auto _ReturnTypeHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)));
502 template<typename _Function, typename _Type>
503 auto _ReturnTypeHelper(_Type t, _Function _Func, int, ...) -> decltype(_Func(t));
504 template<typename _Function, typename _Type>
505 auto _ReturnTypeHelper(_Type t, _Function _Func, ...) -> _BadContinuationParamType;
506
507 template<typename _Function, typename _Type>
508 auto _IsTaskHelper(_Type t, _Function _Func, int, int) -> decltype(_Func(_To_task(t)), std::true_type());
509 template<typename _Function, typename _Type>
510 std::false_type _IsTaskHelper(_Type t, _Function _Func, int, ...);
511
512 template<typename _Function>
513 auto _VoidReturnTypeHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)));
514 template<typename _Function>
515 auto _VoidReturnTypeHelper(_Function _Func, int, ...) -> decltype(_Func());
516
517 template<typename _Function>
518 auto _VoidIsTaskHelper(_Function _Func, int, int) -> decltype(_Func(_To_task_void(_Func)), std::true_type());
519 template<typename _Function>
520 std::false_type _VoidIsTaskHelper(_Function _Func, int, ...);
521
522 template<typename _Function, typename _ExpectedParameterType>
523 struct _FunctionTypeTraits
524 {
525 typedef decltype(
526 _ReturnTypeHelper(stdx::declval<_ExpectedParameterType>(), stdx::declval<_Function>(), 0, 0)) _FuncRetType;
527 static_assert(!std::is_same<_FuncRetType, _BadContinuationParamType>::value,
528 "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or "
529 "task<_ExpectedParameterType> (see below)");
530
531 typedef decltype(
532 _IsTaskHelper(stdx::declval<_ExpectedParameterType>(), stdx::declval<_Function>(), 0, 0)) _Takes_task;
533 };
534
535 template<typename _Function>
536 struct _FunctionTypeTraits<_Function, void>
537 {
538 typedef decltype(_VoidReturnTypeHelper(stdx::declval<_Function>(), 0, 0)) _FuncRetType;
539 typedef decltype(_VoidIsTaskHelper(stdx::declval<_Function>(), 0, 0)) _Takes_task;
540 };
541
542 template<typename _Function, typename _ReturnType>
543 struct _ContinuationTypeTraits
544 {
545 typedef task<
546 typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_FuncRetType>::_TaskRetType>
547 _TaskOfType;
548 };
549
550 // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on
551 // how the variable is declared, the constructor may or may not perform unwrapping. For eg.
552 //
553 // This declaration SHOULD NOT cause unwrapping
554 // task<task<void>> t1([]() -> task<void> {
555 // task<void> t2([]() {});
556 // return t2;
557 // });
558 //
559 // This declaration SHOULD cause unwrapping
560 // task<void>> t1([]() -> task<void> {
561 // task<void> t2([]() {});
562 // return t2;
563 // });
564 // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal
565 // rules apply.
566 template<typename _TaskType, typename _FuncRetType>
567 struct _InitFunctorTypeTraits
568 {
569 typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind;
570 static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask;
571 static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync;
572 };
573
574 template<typename T>
575 struct _InitFunctorTypeTraits<T, T>
576 {
577 typedef _TypeSelectorNoAsync _AsyncKind;
578 static const bool _IsAsyncTask = false;
579 static const bool _IsUnwrappedTaskOrAsync = false;
580 };
581
582 /// <summary>
583 /// Helper object used for LWT invocation.
584 /// </summary>
585 struct _TaskProcThunk
586 {
587 _TaskProcThunk(const std::function<void()>& _Callback) : _M_func(_Callback) {}
588
589 static void _pplx_cdecl _Bridge(void* _PData)
590 {
591 _TaskProcThunk* _PThunk = reinterpret_cast<_TaskProcThunk*>(_PData);
592 _Holder _ThunkHolder(_PThunk);
593 _PThunk->_M_func();
594 }
595
596 private:
597 // RAII holder
598 struct _Holder
599 {
600 _Holder(_TaskProcThunk* _PThunk) : _M_pThunk(_PThunk) {}
601
602 ~_Holder() { delete _M_pThunk; }
603
604 _TaskProcThunk* _M_pThunk;
605
606 private:
607 _Holder& operator=(const _Holder&);
608 };
609
610 std::function<void()> _M_func;
611 _TaskProcThunk& operator=(const _TaskProcThunk&);
612 };
613
614 /// <summary>
615 /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be
616 /// waited on or canceled after scheduling.
617 /// This schedule method will perform automatic inlining base on <paramref value="_InliningMode"/>.
618 /// </summary>
619 /// <param name="_Func">
620 /// The user functor need to be scheduled.
621 /// </param>
622 /// <param name="_InliningMode">
623 /// The inlining scheduling policy for current functor.
624 /// </param>
625 static void _ScheduleFuncWithAutoInline(const std::function<void()>& _Func, _TaskInliningMode_t _InliningMode)
626 {
627 _TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode);
628 }
629
630 class _ContextCallback
631 {
632 typedef std::function<void(void)> _CallbackFunction;
633
634 #if defined(__cplusplus_winrt)
635
636 public:
637 static _ContextCallback _CaptureCurrent()
638 {
639 _ContextCallback _Context;
640 _Context._Capture();
641 return _Context;
642 }
643
644 ~_ContextCallback() { _Reset(); }
645
646 _ContextCallback(bool _DeferCapture = false)
647 {
648 if (_DeferCapture)
649 {
650 _M_context._M_captureMethod = _S_captureDeferred;
651 }
652 else
653 {
654 _M_context._M_pContextCallback = nullptr;
655 }
656 }
657
658 // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context).
659 void _Resolve(bool _CaptureCurrent)
660 {
661 if (_M_context._M_captureMethod == _S_captureDeferred)
662 {
663 _M_context._M_pContextCallback = nullptr;
664
665 if (_CaptureCurrent)
666 {
667 if (_IsCurrentOriginSTA())
668 {
669 _Capture();
670 }
671 #if _UITHREADCTXT_SUPPORT
672 else
673 {
674 // This method will fail if not called from the UI thread.
675 HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback);
676 if (FAILED(_Hr))
677 {
678 _M_context._M_pContextCallback = nullptr;
679 }
680 }
681 #endif /* _UITHREADCTXT_SUPPORT */
682 }
683 }
684 }
685
686 void _Capture()
687 {
688 HRESULT _Hr =
689 CoGetObjectContext(IID_IContextCallback, reinterpret_cast<void**>(&_M_context._M_pContextCallback));
690 if (FAILED(_Hr))
691 {
692 _M_context._M_pContextCallback = nullptr;
693 }
694 }
695
696 _ContextCallback(const _ContextCallback& _Src) { _Assign(_Src._M_context._M_pContextCallback); }
697
698 _ContextCallback(_ContextCallback&& _Src)
699 {
700 _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback;
701 _Src._M_context._M_pContextCallback = nullptr;
702 }
703
704 _ContextCallback& operator=(const _ContextCallback& _Src)
705 {
706 if (this != &_Src)
707 {
708 _Reset();
709 _Assign(_Src._M_context._M_pContextCallback);
710 }
711 return *this;
712 }
713
714 _ContextCallback& operator=(_ContextCallback&& _Src)
715 {
716 if (this != &_Src)
717 {
718 _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback;
719 _Src._M_context._M_pContextCallback = nullptr;
720 }
721 return *this;
722 }
723
724 bool _HasCapturedContext() const
725 {
726 _ASSERTE(_M_context._M_captureMethod != _S_captureDeferred);
727 return (_M_context._M_pContextCallback != nullptr);
728 }
729
730 void _CallInContext(_CallbackFunction _Func) const
731 {
732 if (!_HasCapturedContext())
733 {
734 _Func();
735 }
736 else
737 {
738 ComCallData callData;
739 ZeroMemory(&callData, sizeof(callData));
740 callData.pUserDefined = reinterpret_cast<void*>(&_Func);
741
742 HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(
743 &_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr);
744 if (FAILED(_Hr))
745 {
746 throw ::Platform::Exception::CreateException(_Hr);
747 }
748 }
749 }
750
751 bool operator==(const _ContextCallback& _Rhs) const
752 {
753 return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback);
754 }
755
756 bool operator!=(const _ContextCallback& _Rhs) const { return !(operator==(_Rhs)); }
757
758 private:
759 void _Reset()
760 {
761 if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr)
762 {
763 _M_context._M_pContextCallback->Release();
764 }
765 }
766
767 void _Assign(IContextCallback* _PContextCallback)
768 {
769 _M_context._M_pContextCallback = _PContextCallback;
770 if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr)
771 {
772 _M_context._M_pContextCallback->AddRef();
773 }
774 }
775
776 static HRESULT __stdcall _Bridge(ComCallData* _PParam)
777 {
778 _CallbackFunction* pFunc = reinterpret_cast<_CallbackFunction*>(_PParam->pUserDefined);
779 (*pFunc)();
780 return S_OK;
781 }
782
783 // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations
784 // need know)
785 static bool _IsCurrentOriginSTA()
786 {
787 APTTYPE _AptType;
788 APTTYPEQUALIFIER _AptTypeQualifier;
789
790 HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier);
791 if (SUCCEEDED(hr))
792 {
793 // We determine the origin of a task continuation by looking at where .then is called, so we can tell
794 // whether to need to marshal the continuation back to the originating apartment. If an STA thread is in
795 // executing in a neutral apartment when it schedules a continuation, we will not marshal continuations back
796 // to the STA, since variables used within a neutral apartment are expected to be apartment neutral.
797 switch (_AptType)
798 {
799 case APTTYPE_MAINSTA:
800 case APTTYPE_STA: return true;
801 default: break;
802 }
803 }
804 return false;
805 }
806
807 union {
808 IContextCallback* _M_pContextCallback;
809 size_t _M_captureMethod;
810 } _M_context;
811
812 static const size_t _S_captureDeferred = 1;
813 #else /* defined (__cplusplus_winrt) */
814 public:
815 static _ContextCallback _CaptureCurrent() { return _ContextCallback(); }
816
817 _ContextCallback(bool = false) {}
818
819 _ContextCallback(const _ContextCallback&) {}
820
821 _ContextCallback(_ContextCallback&&) {}
822
823 _ContextCallback& operator=(const _ContextCallback&) { return *this; }
824
825 _ContextCallback& operator=(_ContextCallback&&) { return *this; }
826
827 bool _HasCapturedContext() const { return false; }
828
829 void _Resolve(bool) const {}
830
831 void _CallInContext(_CallbackFunction _Func) const { _Func(); }
832
833 bool operator==(const _ContextCallback&) const { return true; }
834
835 bool operator!=(const _ContextCallback&) const { return false; }
836
837 #endif /* defined (__cplusplus_winrt) */
838 };
839
840 template<typename _Type>
841 struct _ResultHolder
842 {
843 void Set(const _Type& _type) { _Result = _type; }
844
845 _Type Get() { return _Result; }
846
847 _Type _Result;
848 };
849
850 #if defined(__cplusplus_winrt)
851
852 template<typename _Type>
853 struct _ResultHolder<_Type ^>
854 {
855 void Set(_Type ^ const& _type) { _M_Result = _type; }
856
857 _Type ^ Get() { return _M_Result.Get(); } private :
858 // ::Platform::Agile handle specialization of all hats
859 // including ::Platform::String and ::Platform::Array
860 ::Platform::Agile<_Type ^> _M_Result;
861 };
862
863 //
864 // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs.
865 //
866 template<typename _Type>
867 struct _ResultHolder<std::vector<_Type ^>>
868 {
869 void Set(const std::vector<_Type ^>& _type)
870 {
871 _Result.reserve(_type.size());
872
873 for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask)
874 {
875 _Result.emplace_back(*_PTask);
876 }
877 }
878
879 std::vector<_Type ^> Get()
880 {
881 // Return vectory<T^> with the objects that are marshaled in the proper apartment
882 std::vector<_Type ^> _Return;
883 _Return.reserve(_Result.size());
884
885 for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask)
886 {
887 _Return.push_back(
888 _PTask->Get()); // Platform::Agile will marshal the object to appropriate apartment if necessary
889 }
890
891 return _Return;
892 }
893
894 std::vector<::Platform::Agile<_Type ^>> _Result;
895 };
896
897 template<typename _Type>
898 struct _ResultHolder<std::pair<_Type ^, void*>>
899 {
900 void Set(const std::pair<_Type ^, size_t>& _type) { _M_Result = _type; }
901
902 std::pair<_Type ^, size_t> Get() { return std::make_pair(_M_Result.first.Get(), _M_Result.second); }
903
904 private:
905 std::pair<::Platform::Agile<_Type ^>, size_t> _M_Result;
906 };
907
908 #endif /* defined (__cplusplus_winrt) */
909
910 // An exception thrown by the task body is captured in an exception holder and it is shared with all value based
911 // continuations rooted at the task. The exception is 'observed' if the user invokes get()/wait() on any of the tasks
912 // that are sharing this exception holder. If the exception is not observed by the time the internal object owned by the
913 // shared pointer destructs, the process will fail fast.
914 struct _ExceptionHolder
915 {
916 private:
917 void ReportUnhandledError()
918 {
919 #if _MSC_VER >= 1800 && defined(__cplusplus_winrt)
920 if (_M_winRTException != nullptr)
921 {
922 ::Platform::Details::ReportUnhandledError(_M_winRTException);
923 }
924 #endif /* defined (__cplusplus_winrt) */
925 }
926
927 public:
928 explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack& _stackTrace)
929 : _M_exceptionObserved(0)
930 , _M_stdException(_E)
931 , _M_stackTrace(_stackTrace)
932 #if defined(__cplusplus_winrt)
933 , _M_winRTException(nullptr)
934 #endif /* defined (__cplusplus_winrt) */
935 {
936 }
937
938 #if defined(__cplusplus_winrt)
939 explicit _ExceptionHolder(::Platform::Exception ^ _E, const _TaskCreationCallstack& _stackTrace)
940 : _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace)
941 {
942 }
943 #endif /* defined (__cplusplus_winrt) */
944
945 __declspec(noinline) ~_ExceptionHolder()
946 {
947 if (_M_exceptionObserved == 0)
948 {
949 // If you are trapped here, it means an exception thrown in task chain didn't get handled.
950 // Please add task-based continuation to handle all exceptions coming from tasks.
951 // this->_M_stackTrace keeps the creation callstack of the task generates this exception.
952 _REPORT_PPLTASK_UNOBSERVED_EXCEPTION();
953 }
954 }
955
956 void _RethrowUserException()
957 {
958 if (_M_exceptionObserved == 0)
959 {
960 atomic_exchange(_M_exceptionObserved, 1l);
961 }
962
963 #if defined(__cplusplus_winrt)
964 if (_M_winRTException != nullptr)
965 {
966 throw _M_winRTException;
967 }
968 #endif /* defined (__cplusplus_winrt) */
969 std::rethrow_exception(_M_stdException);
970 }
971
972 // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user).
973 // Exceptions that are unobserved when the exception holder is destructed will terminate the process.
974 atomic_long _M_exceptionObserved;
975
976 // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered.
977 std::exception_ptr _M_stdException;
978 #if defined(__cplusplus_winrt)
979 ::Platform::Exception ^ _M_winRTException;
980 #endif /* defined (__cplusplus_winrt) */
981
982 // Disassembling this value will point to a source instruction right after a call instruction. If the call is to
983 // create_task, a task constructor or the then method, the task created by that method is the one that encountered
984 // this exception. If the call is to task_completion_event::set_exception, the set_exception method was the source
985 // of the exception. DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging.
986 _TaskCreationCallstack _M_stackTrace;
987 };
988
989 #if defined(__cplusplus_winrt)
990 /// <summary>
991 /// Base converter class for converting asynchronous interfaces to IAsyncOperation
992 /// </summary>
993 template<typename _AsyncOperationType, typename _CompletionHandlerType, typename _Result>
994 ref struct _AsyncInfoImpl abstract : Windows::Foundation::IAsyncOperation<_Result>
995 {
996 internal :
997 // The async action, action with progress or operation with progress that this stub forwards to.
998 ::Platform::Agile<_AsyncOperationType>
999 _M_asyncInfo;
1000
1001 Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ _M_CompletedHandler;
1002
1003 _AsyncInfoImpl(_AsyncOperationType _AsyncInfo) : _M_asyncInfo(_AsyncInfo) {}
1004
1005 public:
1006 virtual void Cancel() { _M_asyncInfo.Get()->Cancel(); }
1007 virtual void Close() { _M_asyncInfo.Get()->Close(); }
1008
1009 virtual property Windows::Foundation::HResult ErrorCode
1010 {
1011 Windows::Foundation::HResult get() { return _M_asyncInfo.Get()->ErrorCode; }
1012 }
1013
1014 virtual property UINT Id
1015 {
1016 UINT get() { return _M_asyncInfo.Get()->Id; }
1017 }
1018
1019 virtual property Windows::Foundation::AsyncStatus Status
1020 {
1021 Windows::Foundation::AsyncStatus get() { return _M_asyncInfo.Get()->Status; }
1022 }
1023
1024 virtual _Result GetResults() { throw std::runtime_error("derived class must implement"); }
1025
1026 virtual property Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ Completed {
1027 Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ get() { return _M_CompletedHandler; }
1028
1029 void set(Windows::Foundation::AsyncOperationCompletedHandler<_Result> ^ value)
1030 {
1031 _M_CompletedHandler = value;
1032 _M_asyncInfo.Get()->Completed =
1033 ref new _CompletionHandlerType([&](_AsyncOperationType, Windows::Foundation::AsyncStatus status) {
1034 _M_CompletedHandler->Invoke(this, status);
1035 });
1036 }
1037 }
1038 };
1039
1040 /// <summary>
1041 /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of
1042 /// IAsyncOperationWithProgress<T> into IAsyncOperation<T>
1043 /// </summary>
1044 template<typename _Result, typename _Progress>
1045 ref struct _IAsyncOperationWithProgressToAsyncOperationConverter sealed
1046 : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^
1047 , Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result, _Progress>, _Result>
1048 {
1049 internal : _IAsyncOperationWithProgressToAsyncOperationConverter(
1050 Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^ _Operation)
1051 : _AsyncInfoImpl<Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress> ^,
1052 Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_Result, _Progress>,
1053 _Result>(_Operation)
1054 {
1055 }
1056
1057 public:
1058 virtual _Result GetResults() override { return _M_asyncInfo.Get()->GetResults(); }
1059 };
1060
1061 /// <summary>
1062 /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into
1063 /// IAsyncOperation<_Unit_type>
1064 /// </summary>
1065 ref struct _IAsyncActionToAsyncOperationConverter sealed
1066 : _AsyncInfoImpl<Windows::Foundation::IAsyncAction ^
1067 , Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type>
1068 {
1069 internal : _IAsyncActionToAsyncOperationConverter(Windows::Foundation::IAsyncAction ^ _Operation)
1070 : _AsyncInfoImpl<Windows::Foundation::IAsyncAction ^
1071 , Windows::Foundation::AsyncActionCompletedHandler, details::_Unit_type>(_Operation)
1072 {
1073 }
1074
1075 public:
1076 virtual details::_Unit_type GetResults() override
1077 {
1078 // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a
1079 // dummy value.
1080 _M_asyncInfo.Get()->GetResults();
1081 return details::_Unit_type();
1082 }
1083 };
1084
1085 /// <summary>
1086 /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of
1087 /// IAsyncActionWithProgress into IAsyncOperation<_Unit_type>
1088 /// </summary>
1089 template<typename _Progress>
1090 ref struct _IAsyncActionWithProgressToAsyncOperationConverter sealed
1091 : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress> ^
1092 , Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>, details::_Unit_type>
1093 {
1094 internal
1095 : _IAsyncActionWithProgressToAsyncOperationConverter(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^
1096 _Action)
1097 : _AsyncInfoImpl<Windows::Foundation::IAsyncActionWithProgress<_Progress> ^,
1098 Windows::Foundation::AsyncActionWithProgressCompletedHandler<_Progress>,
1099 details::_Unit_type>(_Action)
1100 {
1101 }
1102
1103 public:
1104 virtual details::_Unit_type GetResults() override
1105 {
1106 // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy
1107 // value.
1108 _M_asyncInfo.Get()->GetResults();
1109 return details::_Unit_type();
1110 }
1111 };
1112 #endif /* defined (__cplusplus_winrt) */
1113 } // namespace details
1114
1115 /// <summary>
1116 /// The <c>task_continuation_context</c> class allows you to specify where you would like a continuation to be
1117 /// executed. It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task
1118 /// continuation's execution context is determined by the runtime, and not configurable.
1119 /// </summary>
1120 /// <seealso cref="task Class"/>
1121 /**/
1122 class task_continuation_context : public details::_ContextCallback
1123 {
1124 public:
1125 /// <summary>
1126 /// Creates the default task continuation context.
1127 /// </summary>
1128 /// <returns>
1129 /// The default continuation context.
1130 /// </returns>
1131 /// <remarks>
1132 /// The default context is used if you don't specify a continuation context when you call the <c>then</c>
1133 /// method. In Windows applications for Windows 7 and below, as well as desktop applications on Windows 8 and
1134 /// higher, the runtime determines where task continuations will execute. However, in a Windows Store app, the
1135 /// default continuation context for a continuation on an apartment aware task is the apartment where
1136 /// <c>then</c> is invoked. <para>An apartment aware task is a task that unwraps a Windows Runtime
1137 /// <c>IAsyncInfo</c> interface, or a task that is descended from such a task. Therefore, if you schedule a
1138 /// continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in that
1139 /// STA.</para> <para>A continuation on a non-apartment aware task will execute in a context the Runtime
1140 /// chooses.</para>
1141 /// </remarks>
1142 /**/
1143 static task_continuation_context use_default()
1144 {
1145 #if defined(__cplusplus_winrt)
1146 // The callback context is created with the context set to CaptureDeferred and resolved when it is used in
1147 // .then()
1148 return task_continuation_context(
1149 true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle
1150 #else /* defined (__cplusplus_winrt) */
1151 return task_continuation_context();
1152 #endif /* defined (__cplusplus_winrt) */
1153 }
1154
1155 #if defined(__cplusplus_winrt)
1156 /// <summary>
1157 /// Creates a task continuation context which allows the Runtime to choose the execution context for a
1158 /// continuation.
1159 /// </summary>
1160 /// <returns>
1161 /// A task continuation context that represents an arbitrary location.
1162 /// </returns>
1163 /// <remarks>
1164 /// When this continuation context is used the continuation will execute in a context the runtime chooses even
1165 /// if the antecedent task is apartment aware. <para><c>use_arbitrary</c> can be used to turn off the default
1166 /// behavior for a continuation on an apartment aware task created in an STA. </para> <para>This method is only
1167 /// available to Windows Store apps.</para>
1168 /// </remarks>
1169 /**/
1170 static task_continuation_context use_arbitrary()
1171 {
1172 task_continuation_context _Arbitrary(true);
1173 _Arbitrary._Resolve(false);
1174 return _Arbitrary;
1175 }
1176
1177 /// <summary>
1178 /// Returns a task continuation context object that represents the current execution context.
1179 /// </summary>
1180 /// <returns>
1181 /// The current execution context.
1182 /// </returns>
1183 /// <remarks>
1184 /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right
1185 /// apartment. <para>The value returned by <c>use_current</c> can be used to indicate to the Runtime that the
1186 /// continuation should execute in the captured context (STA vs MTA) regardless of whether or not the antecedent
1187 /// task is apartment aware. An apartment aware task is a task that unwraps a Windows Runtime <c>IAsyncInfo</c>
1188 /// interface, or a task that is descended from such a task. </para> <para>This method is only available to
1189 /// Windows Store apps.</para>
1190 /// </remarks>
1191 /**/
1192 static task_continuation_context use_current()
1193 {
1194 task_continuation_context _Current(true);
1195 _Current._Resolve(true);
1196 return _Current;
1197 }
1198 #endif /* defined (__cplusplus_winrt) */
1199
1200 private:
1201 task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) {}
1202 };
1203
1204 class task_options;
1205 namespace details
1206 {
1207 struct _Internal_task_options
1208 {
1209 bool _M_hasPresetCreationCallstack;
1210 _TaskCreationCallstack _M_presetCreationCallstack;
1211
1212 void _set_creation_callstack(const _TaskCreationCallstack& _callstack)
1213 {
1214 _M_hasPresetCreationCallstack = true;
1215 _M_presetCreationCallstack = _callstack;
1216 }
1217 _Internal_task_options() { _M_hasPresetCreationCallstack = false; }
1218 };
1219
1220 inline _Internal_task_options& _get_internal_task_options(task_options& options);
1221 inline const _Internal_task_options& _get_internal_task_options(const task_options& options);
1222 } // namespace details
1223 /// <summary>
1224 /// Represents the allowed options for creating a task
1225 /// </summary>
1226 class task_options
1227 {
1228 public:
1229 /// <summary>
1230 /// Default list of task creation options
1231 /// </summary>
1232 task_options()
1233 : _M_Scheduler(get_ambient_scheduler())
1234 , _M_CancellationToken(cancellation_token::none())
1235 , _M_ContinuationContext(task_continuation_context::use_default())
1236 , _M_HasCancellationToken(false)
1237 , _M_HasScheduler(false)
1238 {
1239 }
1240
1241 /// <summary>
1242 /// Task option that specify a cancellation token
1243 /// </summary>
1244 task_options(cancellation_token _Token)
1245 : _M_Scheduler(get_ambient_scheduler())
1246 , _M_CancellationToken(_Token)
1247 , _M_ContinuationContext(task_continuation_context::use_default())
1248 , _M_HasCancellationToken(true)
1249 , _M_HasScheduler(false)
1250 {
1251 }
1252
1253 /// <summary>
1254 /// Task option that specify a continuation context. This is valid only for continuations (then)
1255 /// </summary>
1256 task_options(task_continuation_context _ContinuationContext)
1257 : _M_Scheduler(get_ambient_scheduler())
1258 , _M_CancellationToken(cancellation_token::none())
1259 , _M_ContinuationContext(_ContinuationContext)
1260 , _M_HasCancellationToken(false)
1261 , _M_HasScheduler(false)
1262 {
1263 }
1264
1265 /// <summary>
1266 /// Task option that specify a cancellation token and a continuation context. This is valid only for
1267 /// continuations (then)
1268 /// </summary>
1269 task_options(cancellation_token _Token, task_continuation_context _ContinuationContext)
1270 : _M_Scheduler(get_ambient_scheduler())
1271 , _M_CancellationToken(_Token)
1272 , _M_ContinuationContext(_ContinuationContext)
1273 , _M_HasCancellationToken(false)
1274 , _M_HasScheduler(false)
1275 {
1276 }
1277
1278 /// <summary>
1279 /// Task option that specify a scheduler with shared lifetime
1280 /// </summary>
1281 template<typename _SchedType>
1282 task_options(std::shared_ptr<_SchedType> _Scheduler)
1283 : _M_Scheduler(std::move(_Scheduler))
1284 , _M_CancellationToken(cancellation_token::none())
1285 , _M_ContinuationContext(task_continuation_context::use_default())
1286 , _M_HasCancellationToken(false)
1287 , _M_HasScheduler(true)
1288 {
1289 }
1290
1291 /// <summary>
1292 /// Task option that specify a scheduler reference
1293 /// </summary>
1294 task_options(scheduler_interface& _Scheduler)
1295 : _M_Scheduler(&_Scheduler)
1296 , _M_CancellationToken(cancellation_token::none())
1297 , _M_ContinuationContext(task_continuation_context::use_default())
1298 , _M_HasCancellationToken(false)
1299 , _M_HasScheduler(true)
1300 {
1301 }
1302
1303 /// <summary>
1304 /// Task option that specify a scheduler
1305 /// </summary>
1306 task_options(scheduler_ptr _Scheduler)
1307 : _M_Scheduler(std::move(_Scheduler))
1308 , _M_CancellationToken(cancellation_token::none())
1309 , _M_ContinuationContext(task_continuation_context::use_default())
1310 , _M_HasCancellationToken(false)
1311 , _M_HasScheduler(true)
1312 {
1313 }
1314
1315 /// <summary>
1316 /// Task option copy constructor
1317 /// </summary>
1318 task_options(const task_options& _TaskOptions)
1319 : _M_Scheduler(_TaskOptions.get_scheduler())
1320 , _M_CancellationToken(_TaskOptions.get_cancellation_token())
1321 , _M_ContinuationContext(_TaskOptions.get_continuation_context())
1322 , _M_HasCancellationToken(_TaskOptions.has_cancellation_token())
1323 , _M_HasScheduler(_TaskOptions.has_scheduler())
1324 {
1325 }
1326
1327 /// <summary>
1328 /// Sets the given token in the options
1329 /// </summary>
1330 void set_cancellation_token(cancellation_token _Token)
1331 {
1332 _M_CancellationToken = _Token;
1333 _M_HasCancellationToken = true;
1334 }
1335
1336 /// <summary>
1337 /// Sets the given continuation context in the options
1338 /// </summary>
1339 void set_continuation_context(task_continuation_context _ContinuationContext)
1340 {
1341 _M_ContinuationContext = _ContinuationContext;
1342 }
1343
1344 /// <summary>
1345 /// Indicates whether a cancellation token was specified by the user
1346 /// </summary>
1347 bool has_cancellation_token() const { return _M_HasCancellationToken; }
1348
1349 /// <summary>
1350 /// Returns the cancellation token
1351 /// </summary>
1352 cancellation_token get_cancellation_token() const { return _M_CancellationToken; }
1353
1354 /// <summary>
1355 /// Returns the continuation context
1356 /// </summary>
1357 task_continuation_context get_continuation_context() const { return _M_ContinuationContext; }
1358
1359 /// <summary>
1360 /// Indicates whether a scheduler n was specified by the user
1361 /// </summary>
1362 bool has_scheduler() const { return _M_HasScheduler; }
1363
1364 /// <summary>
1365 /// Returns the scheduler
1366 /// </summary>
1367 scheduler_ptr get_scheduler() const { return _M_Scheduler; }
1368
1369 private:
1370 task_options const& operator=(task_options const& _Right);
1371 friend details::_Internal_task_options& details::_get_internal_task_options(task_options&);
1372 friend const details::_Internal_task_options& details::_get_internal_task_options(const task_options&);
1373
1374 scheduler_ptr _M_Scheduler;
1375 cancellation_token _M_CancellationToken;
1376 task_continuation_context _M_ContinuationContext;
1377 details::_Internal_task_options _M_InternalTaskOptions;
1378 bool _M_HasCancellationToken;
1379 bool _M_HasScheduler;
1380 };
1381
1382 namespace details
1383 {
1384 inline _Internal_task_options& _get_internal_task_options(task_options& options)
1385 {
1386 return options._M_InternalTaskOptions;
1387 }
1388 inline const _Internal_task_options& _get_internal_task_options(const task_options& options)
1389 {
1390 return options._M_InternalTaskOptions;
1391 }
1392
1393 struct _Task_impl_base;
1394 template<typename _ReturnType>
1395 struct _Task_impl;
1396
1397 template<typename _ReturnType>
1398 struct _Task_ptr
1399 {
1400 typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type;
1401 static _Type _Make(_CancellationTokenState* _Ct, scheduler_ptr _Scheduler_arg)
1402 {
1403 return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg);
1404 }
1405 };
1406
1407 typedef _TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t;
1408 typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base;
1409
1410 // The weak-typed base task handler for continuation tasks.
1411 struct _ContinuationTaskHandleBase : _UnrealizedChore_t
1412 {
1413 _ContinuationTaskHandleBase* _M_next;
1414 task_continuation_context _M_continuationContext;
1415 bool _M_isTaskBasedContinuation;
1416
1417 // This field gives inlining scheduling policy for current chore.
1418 _TaskInliningMode_t _M_inliningMode;
1419
1420 virtual _Task_ptr_base _GetTaskImplBase() const = 0;
1421
1422 _ContinuationTaskHandleBase()
1423 : _M_next(nullptr)
1424 , _M_continuationContext(task_continuation_context::use_default())
1425 , _M_isTaskBasedContinuation(false)
1426 , _M_inliningMode(details::_NoInline)
1427 {
1428 }
1429
1430 virtual ~_ContinuationTaskHandleBase() {}
1431 };
1432
1433 #if PPLX_TASK_ASYNC_LOGGING
1434 // GUID used for identifying causality logs from PPLTask
1435 const ::Platform::Guid _PPLTaskCausalityPlatformID(
1436 0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE);
1437
1438 __declspec(selectany) volatile long _isCausalitySupported = 0;
1439
1440 inline bool _IsCausalitySupported()
1441 {
1442 #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
1443 if (_isCausalitySupported == 0)
1444 {
1445 long _causality = 1;
1446 OSVERSIONINFOEX _osvi = {};
1447 _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
1448
1449 // The Causality is supported on Windows version higher than Windows 8
1450 _osvi.dwMajorVersion = 6;
1451 _osvi.dwMinorVersion = 3;
1452
1453 DWORDLONG _conditionMask = 0;
1454 VER_SET_CONDITION(_conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
1455 VER_SET_CONDITION(_conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
1456
1457 if (::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask))
1458 {
1459 _causality = 2;
1460 }
1461
1462 _isCausalitySupported = _causality;
1463 return _causality == 2;
1464 }
1465
1466 return _isCausalitySupported == 2 ? true : false;
1467 #else
1468 return true;
1469 #endif
1470 }
1471
1472 // Stateful logger rests inside task_impl_base.
1473 struct _TaskEventLogger
1474 {
1475 _Task_impl_base* _M_task;
1476 bool _M_scheduled;
1477 bool _M_taskPostEventStarted;
1478
1479 // Log before scheduling task
1480 void _LogScheduleTask(bool _isContinuation)
1481 {
1482 if (details::_IsCausalitySupported())
1483 {
1484 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(
1485 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
1486 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1487 _PPLTaskCausalityPlatformID,
1488 reinterpret_cast<unsigned long long>(_M_task),
1489 _isContinuation ? "pplx::PPLTask::ScheduleContinuationTask" : "pplx::PPLTask::ScheduleTask",
1490 0);
1491 _M_scheduled = true;
1492 }
1493 }
1494
1495 // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which
1496 // includes cancel state.
1497 void _LogCancelTask()
1498 {
1499 if (details::_IsCausalitySupported())
1500 {
1501 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(
1502 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important,
1503 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1504 _PPLTaskCausalityPlatformID,
1505 reinterpret_cast<unsigned long long>(_M_task),
1506 ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel);
1507 }
1508 }
1509
1510 // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception)
1511 // without having run
1512 void _LogTaskCompleted();
1513
1514 // Log when task body (which includes user lambda and other scheduling code) begin to run
1515 void _LogTaskExecutionStarted() {}
1516
1517 // Log when task body finish executing
1518 void _LogTaskExecutionCompleted()
1519 {
1520 if (_M_taskPostEventStarted && details::_IsCausalitySupported())
1521 {
1522 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(
1523 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
1524 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1525 ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification);
1526 }
1527 }
1528
1529 // Log right before user lambda being invoked
1530 void _LogWorkItemStarted()
1531 {
1532 if (details::_IsCausalitySupported())
1533 {
1534 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(
1535 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
1536 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1537 _PPLTaskCausalityPlatformID,
1538 reinterpret_cast<unsigned long long>(_M_task),
1539 ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution);
1540 }
1541 }
1542
1543 // Log right after user lambda being invoked
1544 void _LogWorkItemCompleted()
1545 {
1546 if (details::_IsCausalitySupported())
1547 {
1548 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(
1549 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
1550 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1551 ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution);
1552
1553 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(
1554 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
1555 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
1556 _PPLTaskCausalityPlatformID,
1557 reinterpret_cast<unsigned long long>(_M_task),
1558 ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification);
1559 _M_taskPostEventStarted = true;
1560 }
1561 }
1562
1563 _TaskEventLogger(_Task_impl_base* _task) : _M_task(_task)
1564 {
1565 _M_scheduled = false;
1566 _M_taskPostEventStarted = false;
1567 }
1568 };
1569
1570 // Exception safe logger for user lambda
1571 struct _TaskWorkItemRAIILogger
1572 {
1573 _TaskEventLogger& _M_logger;
1574 _TaskWorkItemRAIILogger(_TaskEventLogger& _taskHandleLogger) : _M_logger(_taskHandleLogger)
1575 {
1576 _M_logger._LogWorkItemStarted();
1577 }
1578
1579 ~_TaskWorkItemRAIILogger() { _M_logger._LogWorkItemCompleted(); }
1580 _TaskWorkItemRAIILogger& operator=(const _TaskWorkItemRAIILogger&); // cannot be assigned
1581 };
1582
1583 #else
1584 inline void _LogCancelTask(_Task_impl_base*) {}
1585 struct _TaskEventLogger
1586 {
1587 void _LogScheduleTask(bool) {}
1588 void _LogCancelTask() {}
1589 void _LogWorkItemStarted() {}
1590 void _LogWorkItemCompleted() {}
1591 void _LogTaskExecutionStarted() {}
1592 void _LogTaskExecutionCompleted() {}
1593 void _LogTaskCompleted() {}
1594 _TaskEventLogger(_Task_impl_base*) {}
1595 };
1596 struct _TaskWorkItemRAIILogger
1597 {
1598 _TaskWorkItemRAIILogger(_TaskEventLogger&) {}
1599 };
1600 #endif
1601
1602 /// <summary>
1603 /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task
1604 /// handler to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial
1605 /// tasks and continuation tasks. For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore_t, and for
1606 /// continuation tasks, it will be derived from _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle
1607 /// object is be managed by runtime if task handle is scheduled.
1608 /// </summary>
1609 /// <typeparam name="_ReturnType">
1610 /// The result type of the _Task_impl.
1611 /// </typeparam>
1612 /// <typeparam name="_DerivedTaskHandle">
1613 /// The derived task handle class. The <c>operator ()</c> needs to be implemented.
1614 /// </typeparam>
1615 /// <typeparam name="_BaseTaskHandle">
1616 /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore_t or
1617 /// _ContinuationTaskHandleBase.
1618 /// </typeparam>
1619 template<typename _ReturnType, typename _DerivedTaskHandle, typename _BaseTaskHandle>
1620 struct _PPLTaskHandle : _BaseTaskHandle
1621 {
1622 _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type& _PTask) : _M_pTask(_PTask) {}
1623
1624 virtual ~_PPLTaskHandle()
1625 {
1626 // Here is the sink of all task completion code paths
1627 _M_pTask->_M_taskEventLogger._LogTaskCompleted();
1628 }
1629
1630 virtual void invoke() const
1631 {
1632 // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled
1633 // by the runtime.
1634 _ASSERTE((bool)_M_pTask);
1635 if (!_M_pTask->_TransitionedToStarted())
1636 {
1637 static_cast<const _DerivedTaskHandle*>(this)->_SyncCancelAndPropagateException();
1638 return;
1639 }
1640
1641 _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted();
1642 try
1643 {
1644 // All derived task handle must implement this contract function.
1645 static_cast<const _DerivedTaskHandle*>(this)->_Perform();
1646 }
1647 catch (const task_canceled&)
1648 {
1649 _M_pTask->_Cancel(true);
1650 }
1651 catch (const _Interruption_exception&)
1652 {
1653 _M_pTask->_Cancel(true);
1654 }
1655 #if defined(__cplusplus_winrt)
1656 catch (::Platform::Exception ^ _E)
1657 {
1658 _M_pTask->_CancelWithException(_E);
1659 }
1660 #endif /* defined (__cplusplus_winrt) */
1661 catch (...)
1662 {
1663 _M_pTask->_CancelWithException(std::current_exception());
1664 }
1665 _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted();
1666 }
1667
1668 // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase.
1669 // The return value should be automatically optimized by R-value ref.
1670 _Task_ptr_base _GetTaskImplBase() const { return _M_pTask; }
1671
1672 typename _Task_ptr<_ReturnType>::_Type _M_pTask;
1673
1674 private:
1675 _PPLTaskHandle const& operator=(_PPLTaskHandle const&); // no assignment operator
1676 };
1677
1678 /// <summary>
1679 /// The base implementation of a first-class task. This class contains all the non-type specific
1680 /// implementation details of the task.
1681 /// </summary>
1682 /**/
1683 struct _Task_impl_base
1684 {
1685 enum _TaskInternalState
1686 {
1687 // Tracks the state of the task, rather than the task collection on which the task is scheduled
1688 _Created,
1689 _Started,
1690 _PendingCancel,
1691 _Completed,
1692 _Canceled
1693 };
1694 // _M_taskEventLogger - 'this' : used in base member initializer list
1695 #if defined(_MSC_VER)
1696 #pragma warning(push)
1697 #pragma warning(disable : 4355)
1698 #endif
1699 _Task_impl_base(_CancellationTokenState* _PTokenState, scheduler_ptr _Scheduler_arg)
1700 : _M_TaskState(_Created)
1701 , _M_fFromAsync(false)
1702 , _M_fUnwrappedTask(false)
1703 , _M_pRegistration(nullptr)
1704 , _M_Continuations(nullptr)
1705 , _M_TaskCollection(_Scheduler_arg)
1706 , _M_taskEventLogger(this)
1707 {
1708 // Set cancellation token
1709 _M_pTokenState = _PTokenState;
1710 _ASSERTE(_M_pTokenState != nullptr);
1711 if (_M_pTokenState != _CancellationTokenState::_None()) _M_pTokenState->_Reference();
1712 }
1713 #if defined(_MSC_VER)
1714 #pragma warning(pop)
1715 #endif
1716
1717 virtual ~_Task_impl_base()
1718 {
1719 _ASSERTE(_M_pTokenState != nullptr);
1720 if (_M_pTokenState != _CancellationTokenState::_None())
1721 {
1722 _M_pTokenState->_Release();
1723 }
1724 }
1725
1726 task_status _Wait()
1727 {
1728 bool _DoWait = true;
1729
1730 #if defined(__cplusplus_winrt)
1731 if (_IsNonBlockingThread())
1732 {
1733 // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is
1734 // illegal if task has not been completed.
1735 if (!_IsCompleted() && !_IsCanceled())
1736 {
1737 throw invalid_operation("Illegal to wait on a task in a Windows Runtime STA");
1738 }
1739 else
1740 {
1741 // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task
1742 // group. If a continuation needs to be marshaled to a different apartment, instead of scheduling, we
1743 // make a synchronous cross apartment COM call to execute the continuation. If it then happens to do
1744 // something which waits on the ancestor (say it calls .get(), which task based continuations are wont
1745 // to do), waiting on the task group results in on the chore that is making this synchronous callback,
1746 // which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on
1747 // if it has finished execution (which means now we are on the inline synchronous callback).
1748 _DoWait = false;
1749 }
1750 }
1751 #endif /* defined (__cplusplus_winrt) */
1752 if (_DoWait)
1753 {
1754 // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The
1755 // async operation will take place on a thread in the appropriate apartment Simply wait for the completed
1756 // event to be set.
1757 if (_M_fFromAsync)
1758 {
1759 _M_TaskCollection._Wait();
1760 }
1761 else
1762 {
1763 // Wait on the task collection to complete. The task collection is guaranteed to still be
1764 // valid since the task must be still within scope so that the _Task_impl_base destructor
1765 // has not yet been called. This call to _Wait potentially inlines execution of work.
1766 try
1767 {
1768 // Invoking wait on a task collection resets the state of the task collection. This means that
1769 // if the task collection itself were canceled, or had encountered an exception, only the first
1770 // call to wait will receive this status. However, both cancellation and exceptions flowing through
1771 // tasks set state in the task impl itself.
1772
1773 // When it returns canceled, either work chore or the cancel thread should already have set task's
1774 // state properly -- canceled state or completed state (because there was no interruption point).
1775 // For tasks with unwrapped tasks, we should not change the state of current task, since the
1776 // unwrapped task are still running.
1777 _M_TaskCollection._RunAndWait();
1778 }
1779 catch (details::_Interruption_exception&)
1780 {
1781 // The _TaskCollection will never be an interruption point since it has a none token.
1782 _ASSERTE(false);
1783 }
1784 catch (task_canceled&)
1785 {
1786 // task_canceled is a special exception thrown by cancel_current_task. The spec states that
1787 // cancel_current_task must be called from code that is executed within the task (throwing it from
1788 // parallel work created by and waited upon by the task is acceptable). We can safely assume that
1789 // the task wrapper _PPLTaskHandle::operator() has seen the exception and canceled the task. Swallow
1790 // the exception here.
1791 _ASSERTE(_IsCanceled());
1792 }
1793 #if defined(__cplusplus_winrt)
1794 catch (::Platform::Exception ^ _E)
1795 {
1796 // Its possible the task body hasn't seen the exception, if so we need to cancel with exception
1797 // here.
1798 if (!_HasUserException())
1799 {
1800 _CancelWithException(_E);
1801 }
1802 // Rethrow will mark the exception as observed.
1803 _M_exceptionHolder->_RethrowUserException();
1804 }
1805 #endif /* defined (__cplusplus_winrt) */
1806 catch (...)
1807 {
1808 // Its possible the task body hasn't seen the exception, if so we need to cancel with exception
1809 // here.
1810 if (!_HasUserException())
1811 {
1812 _CancelWithException(std::current_exception());
1813 }
1814 // Rethrow will mark the exception as observed.
1815 _M_exceptionHolder->_RethrowUserException();
1816 }
1817
1818 // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a
1819 // task which is to be unwrapped and plumbed to the output of this task, we must not only wait on the
1820 // lambda body, we must wait on the **INNER** body. It is in theory possible that we could inline such
1821 // if we plumb a series of things through; however, this takes the tact of simply waiting upon the
1822 // completion signal.
1823 if (_M_fUnwrappedTask)
1824 {
1825 _M_TaskCollection._Wait();
1826 }
1827 }
1828 }
1829
1830 if (_HasUserException())
1831 {
1832 _M_exceptionHolder->_RethrowUserException();
1833 }
1834 else if (_IsCanceled())
1835 {
1836 return canceled;
1837 }
1838 _ASSERTE(_IsCompleted());
1839 return completed;
1840 }
1841
1842 /// <summary>
1843 /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal
1844 /// state.
1845 /// </summary>
1846 /// <param name="_SynchronousCancel">
1847 /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an
1848 /// ancestor or task_completion_event the task was registered with were canceled with an exception. A
1849 /// synchronous cancel is one that assures the task could not be running on a different thread at the time the
1850 /// cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no
1851 /// control over the thread that could be executing the task, that is the task could execute concurrently while
1852 /// the cancellation is in progress.
1853 /// </param>
1854 /// <param name="_UserException">
1855 /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation.
1856 /// </param>
1857 /// <param name="_PropagatedFromAncestor">
1858 /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that
1859 /// was encountered by the task itself. Only valid when _UserException is set to true.
1860 /// </param>
1861 /// <param name="_ExHolder">
1862 /// The exception holder that represents the exception. Only valid when _UserException is set to true.
1863 /// </param>
1864 virtual bool _CancelAndRunContinuations(bool _SynchronousCancel,
1865 bool _UserException,
1866 bool _PropagatedFromAncestor,
1867 const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0;
1868
1869 bool _Cancel(bool _SynchronousCancel)
1870 {
1871 // Send in a dummy value for exception. It is not used when the first parameter is false.
1872 return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder);
1873 }
1874
1875 bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor)
1876 {
1877 // This task was canceled because an ancestor task encountered an exception.
1878 return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder);
1879 }
1880
1881 #if defined(__cplusplus_winrt)
1882 bool _CancelWithException(::Platform::Exception ^ _Exception)
1883 {
1884 // This task was canceled because the task body encountered an exception.
1885 _ASSERTE(!_HasUserException());
1886 return _CancelAndRunContinuations(
1887 true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack()));
1888 }
1889 #endif /* defined (__cplusplus_winrt) */
1890
1891 bool _CancelWithException(const std::exception_ptr& _Exception)
1892 {
1893 // This task was canceled because the task body encountered an exception.
1894 _ASSERTE(!_HasUserException());
1895 return _CancelAndRunContinuations(
1896 true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack()));
1897 }
1898
1899 void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr)
1900 {
1901 _ASSERTE(details::_CancellationTokenState::_IsValid(_M_pTokenState));
1902
1903 auto _CancellationCallback = [_WeakPtr]() {
1904 // Taking ownership of the task prevents dead lock during destruction
1905 // if the destructor waits for the cancellations to be finished
1906 auto _task = _WeakPtr.lock();
1907 if (_task != nullptr) _task->_Cancel(false);
1908 };
1909
1910 _M_pRegistration =
1911 new details::_CancellationTokenCallback<decltype(_CancellationCallback)>(_CancellationCallback);
1912 _M_pTokenState->_RegisterCallback(_M_pRegistration);
1913 }
1914
1915 void _DeregisterCancellation()
1916 {
1917 if (_M_pRegistration != nullptr)
1918 {
1919 _M_pTokenState->_DeregisterCallback(_M_pRegistration);
1920 _M_pRegistration->_Release();
1921 _M_pRegistration = nullptr;
1922 }
1923 }
1924
1925 bool _IsCreated() { return (_M_TaskState == _Created); }
1926
1927 bool _IsStarted() { return (_M_TaskState == _Started); }
1928
1929 bool _IsPendingCancel() { return (_M_TaskState == _PendingCancel); }
1930
1931 bool _IsCompleted() { return (_M_TaskState == _Completed); }
1932
1933 bool _IsCanceled() { return (_M_TaskState == _Canceled); }
1934
1935 bool _HasUserException() { return static_cast<bool>(_M_exceptionHolder); }
1936
1937 const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder()
1938 {
1939 _ASSERTE(_HasUserException());
1940 return _M_exceptionHolder;
1941 }
1942
1943 bool _IsApartmentAware() { return _M_fFromAsync; }
1944
1945 void _SetAsync(bool _Async = true) { _M_fFromAsync = _Async; }
1946
1947 _TaskCreationCallstack _GetTaskCreationCallstack() { return _M_pTaskCreationCallstack; }
1948
1949 void _SetTaskCreationCallstack(const _TaskCreationCallstack& _Callstack) { _M_pTaskCreationCallstack = _Callstack; }
1950
1951 /// <summary>
1952 /// Helper function to schedule the task on the Task Collection.
1953 /// </summary>
1954 /// <param name="_PTaskHandle">
1955 /// The task chore handle that need to be executed.
1956 /// </param>
1957 /// <param name="_InliningMode">
1958 /// The inlining scheduling policy for current _PTaskHandle.
1959 /// </param>
1960 void _ScheduleTask(_UnrealizedChore_t* _PTaskHandle, _TaskInliningMode_t _InliningMode)
1961 {
1962 try
1963 {
1964 _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode);
1965 }
1966 catch (const task_canceled&)
1967 {
1968 // task_canceled is a special exception thrown by cancel_current_task. The spec states that
1969 // cancel_current_task must be called from code that is executed within the task (throwing it from parallel
1970 // work created by and waited upon by the task is acceptable). We can safely assume that the task wrapper
1971 // _PPLTaskHandle::operator() has seen the exception and canceled the task. Swallow the exception here.
1972 _ASSERTE(_IsCanceled());
1973 }
1974 catch (const _Interruption_exception&)
1975 {
1976 // The _TaskCollection will never be an interruption point since it has a none token.
1977 _ASSERTE(false);
1978 }
1979 catch (...)
1980 {
1981 // The exception could have come from two places:
1982 // 1. From the chore body, so it already should have been caught and canceled.
1983 // In this case swallow the exception.
1984 // 2. From trying to actually schedule the task on the scheduler.
1985 // In this case cancel the task with the current exception, otherwise the
1986 // task will never be signaled leading to deadlock when waiting on the task.
1987 if (!_HasUserException())
1988 {
1989 _CancelWithException(std::current_exception());
1990 }
1991 }
1992 }
1993
1994 /// <summary>
1995 /// Function executes a continuation. This function is recorded by a parent task implementation
1996 /// when a continuation is created in order to execute later.
1997 /// </summary>
1998 /// <param name="_PTaskHandle">
1999 /// The continuation task chore handle that need to be executed.
2000 /// </param>
2001 /**/
2002 void _RunContinuation(_ContinuationTaskHandleBase* _PTaskHandle)
2003 {
2004 _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase();
2005 if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation)
2006 {
2007 if (_HasUserException())
2008 {
2009 // If the ancestor encountered an exception, transfer the exception to the continuation
2010 // This traverses down the tree to propagate the exception.
2011 _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true);
2012 }
2013 else
2014 {
2015 // If the ancestor was canceled, then your own execution should be canceled.
2016 // This traverses down the tree to cancel it.
2017 _ImplBase->_Cancel(true);
2018 }
2019 }
2020 else
2021 {
2022 // This can only run when the ancestor has completed or it's a task based continuation that fires when a
2023 // task is canceled (with or without a user exception).
2024 _ASSERTE(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation);
2025 _ASSERTE(!_ImplBase->_IsCanceled());
2026 return _ImplBase->_ScheduleContinuationTask(_PTaskHandle);
2027 }
2028
2029 // If the handle is not scheduled, we need to manually delete it.
2030 delete _PTaskHandle;
2031 }
2032
2033 // Schedule a continuation to run
2034 void _ScheduleContinuationTask(_ContinuationTaskHandleBase* _PTaskHandle)
2035 {
2036 _M_taskEventLogger._LogScheduleTask(true);
2037 // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a
2038 // different Windows Runtime apartment)
2039 if (_PTaskHandle->_M_continuationContext._HasCapturedContext())
2040 {
2041 // For those continuations need to be scheduled inside captured context, we will try to apply automatic
2042 // inlining to their inline modes, if they haven't been specified as _ForceInline yet. This change will
2043 // encourage those continuations to be executed inline so that reduce the cost of marshaling. For normal
2044 // continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl
2045 // method.
2046 if (_PTaskHandle->_M_inliningMode != details::_ForceInline)
2047 {
2048 _PTaskHandle->_M_inliningMode = details::_DefaultAutoInline;
2049 }
2050 _ScheduleFuncWithAutoInline(
2051 [_PTaskHandle]() {
2052 // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a
2053 // shared_ptr to the _Task_impl_base. Because "this" pointer will be invalid as soon as _PTaskHandle
2054 // get deleted. _PTaskHandle will be deleted after being scheduled.
2055 auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase();
2056 if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext)
2057 {
2058 _TaskImplPtr->_ScheduleTask(_PTaskHandle, details::_ForceInline);
2059 }
2060 else
2061 {
2062 //
2063 // It's entirely possible that the attempt to marshal the call into a differing context will
2064 // fail. In this case, we need to handle the exception and mark the continuation as canceled
2065 // with the appropriate exception. There is one slight hitch to this:
2066 //
2067 // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS.
2068 // This will in effect turn an SEH into a C++ exception that gets tagged on the task. One
2069 // unfortunate result of this is that various pieces of the task infrastructure will not be in a
2070 // valid state after this in /EHsc (due to the lack of destructors running, etc...).
2071 //
2072 try
2073 {
2074 // Dev10 compiler needs this!
2075 auto _PTaskHandle1 = _PTaskHandle;
2076 _PTaskHandle->_M_continuationContext._CallInContext([_PTaskHandle1, _TaskImplPtr]() {
2077 _TaskImplPtr->_ScheduleTask(_PTaskHandle1, details::_ForceInline);
2078 });
2079 }
2080 #if defined(__cplusplus_winrt)
2081 catch (::Platform::Exception ^ _E)
2082 {
2083 _TaskImplPtr->_CancelWithException(_E);
2084 }
2085 #endif /* defined (__cplusplus_winrt) */
2086 catch (...)
2087 {
2088 _TaskImplPtr->_CancelWithException(std::current_exception());
2089 }
2090 }
2091 },
2092 _PTaskHandle->_M_inliningMode);
2093 }
2094 else
2095 {
2096 _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode);
2097 }
2098 }
2099
2100 /// <summary>
2101 /// Schedule the actual continuation. This will either schedule the function on the continuation task's
2102 /// implementation if the task has completed or append it to a list of functions to execute when the task
2103 /// actually does complete.
2104 /// </summary>
2105 /// <typeparam name="_FuncInputType">
2106 /// The input type of the task.
2107 /// </typeparam>
2108 /// <typeparam name="_FuncOutputType">
2109 /// The output type of the task.
2110 /// </typeparam>
2111 /**/
2112 void _ScheduleContinuation(_ContinuationTaskHandleBase* _PTaskHandle)
2113 {
2114 enum
2115 {
2116 _Nothing,
2117 _Schedule,
2118 _Cancel,
2119 _CancelWithException
2120 } _Do = _Nothing;
2121
2122 // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right
2123 // away. Otherwise, add it to the list of pending continuations
2124 {
2125 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);
2126 if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation))
2127 {
2128 _Do = _Schedule;
2129 }
2130 else if (_IsCanceled())
2131 {
2132 if (_HasUserException())
2133 {
2134 _Do = _CancelWithException;
2135 }
2136 else
2137 {
2138 _Do = _Cancel;
2139 }
2140 }
2141 else
2142 {
2143 // chain itself on the continuation chain.
2144 _PTaskHandle->_M_next = _M_Continuations;
2145 _M_Continuations = _PTaskHandle;
2146 }
2147 }
2148
2149 // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off
2150 // of async tasks may execute inline.
2151 switch (_Do)
2152 {
2153 case _Schedule:
2154 {
2155 _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle);
2156 break;
2157 }
2158 case _Cancel:
2159 {
2160 // If the ancestor was canceled, then your own execution should be canceled.
2161 // This traverses down the tree to cancel it.
2162 _PTaskHandle->_GetTaskImplBase()->_Cancel(true);
2163
2164 delete _PTaskHandle;
2165 break;
2166 }
2167 case _CancelWithException:
2168 {
2169 // If the ancestor encountered an exception, transfer the exception to the continuation
2170 // This traverses down the tree to propagate the exception.
2171 _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true);
2172
2173 delete _PTaskHandle;
2174 break;
2175 }
2176 case _Nothing:
2177 default:
2178 // In this case, we have inserted continuation to continuation chain,
2179 // nothing more need to be done, just leave.
2180 break;
2181 }
2182 }
2183
2184 void _RunTaskContinuations()
2185 {
2186 // The link list can no longer be modified at this point,
2187 // since all following up continuations will be scheduled by themselves.
2188 _ContinuationList _Cur = _M_Continuations, _Next;
2189 _M_Continuations = nullptr;
2190 while (_Cur)
2191 {
2192 // Current node might be deleted after running,
2193 // so we must fetch the next first.
2194 _Next = _Cur->_M_next;
2195 _RunContinuation(_Cur);
2196 _Cur = _Next;
2197 }
2198 }
2199
2200 #if defined(__cplusplus_winrt)
2201 static bool _IsNonBlockingThread()
2202 {
2203 APTTYPE _AptType;
2204 APTTYPEQUALIFIER _AptTypeQualifier;
2205
2206 HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier);
2207 //
2208 // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure.
2209 //
2210 if (SUCCEEDED(hr))
2211 {
2212 switch (_AptType)
2213 {
2214 case APTTYPE_STA:
2215 case APTTYPE_MAINSTA: return true; break;
2216 case APTTYPE_NA:
2217 switch (_AptTypeQualifier)
2218 {
2219 // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is
2220 // allowed to wait, we check the app qualifier. If it is an STA thread executing in a neutral
2221 // apartment, waiting is illegal, because the thread is responsible for pumping messages and
2222 // waiting on a task could take the thread out of circulation for a while.
2223 case APTTYPEQUALIFIER_NA_ON_STA:
2224 case APTTYPEQUALIFIER_NA_ON_MAINSTA: return true; break;
2225 }
2226 break;
2227 }
2228 }
2229
2230 #if _UITHREADCTXT_SUPPORT
2231 // This method is used to throw an exception in _Wait() if called within STA. We
2232 // want the same behavior if _Wait is called on the UI thread.
2233 if (SUCCEEDED(CaptureUiThreadContext(nullptr)))
2234 {
2235 return true;
2236 }
2237 #endif /* _UITHREADCTXT_SUPPORT */
2238
2239 return false;
2240 }
2241
2242 template<typename _ReturnType, typename>
2243 static void _AsyncInit(
2244 const typename _Task_ptr<_ReturnType>::_Type& _OuterTask,
2245 Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)
2246 {
2247 // This method is invoked either when a task is created from an existing async operation or
2248 // when a lambda that creates an async operation executes.
2249
2250 // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM
2251 // reference on the IAsyncInfo object will be released when all ^references to the operation go out of scope.
2252
2253 // This assertion uses the existence of taskcollection to determine if the task was created from an event.
2254 // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection
2255 // when a custom scheduler is used.
2256 // _ASSERTE((!_OuterTask->_M_TaskCollection._IsCreated() || _OuterTask->_M_fUnwrappedTask) &&
2257 // !_OuterTask->_IsCanceled());
2258
2259 // Pass the shared_ptr by value into the lambda instead of using 'this'.
2260 _AsyncOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType>(
2261 [_OuterTask](
2262 Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^
2263 _Operation,
2264 Windows::Foundation::AsyncStatus _Status) mutable {
2265 if (_Status == Windows::Foundation::AsyncStatus::Canceled)
2266 {
2267 _OuterTask->_Cancel(true);
2268 }
2269 else if (_Status == Windows::Foundation::AsyncStatus::Error)
2270 {
2271 _OuterTask->_CancelWithException(
2272 ::Platform::Exception::ReCreateException(static_cast<int>(_Operation->ErrorCode.Value)));
2273 }
2274 else
2275 {
2276 _ASSERTE(_Status == Windows::Foundation::AsyncStatus::Completed);
2277 _OuterTask->_FinalizeAndRunContinuations(_Operation->GetResults());
2278 }
2279
2280 // Take away this shared pointers reference on the task instead of waiting for the delegate to be
2281 // released. It could be released on a different thread after a delay, and not releasing the reference
2282 // here could cause the tasks to hold on to resources longer than they should. As an example, without
2283 // this reset, writing to a file followed by reading from it using the Windows Runtime Async APIs causes
2284 // a sharing violation. Using const_cast is the workaround for failed mutable keywords
2285 const_cast<_Task_ptr<_ReturnType>::_Type&>(_OuterTask).reset();
2286 });
2287 _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp);
2288 }
2289 #endif /* defined (__cplusplus_winrt) */
2290
2291 template<typename _ReturnType, typename _InternalReturnType>
2292 static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask,
2293 const task<_InternalReturnType>& _UnwrappedTask)
2294 {
2295 _ASSERTE(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled());
2296
2297 //
2298 // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in
2299 // the presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception
2300 // handling continuation off the inner task which does the appropriate funneling to the outer one. We use _Then
2301 // instead of then to prevent the exception from being marked as observed by our internal continuation. This
2302 // continuation must be scheduled regardless of whether or not the _OuterTask task is canceled.
2303 //
2304 _UnwrappedTask._Then(
2305 [_OuterTask](task<_InternalReturnType> _AncestorTask) {
2306 if (_AncestorTask._GetImpl()->_IsCompleted())
2307 {
2308 _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult());
2309 }
2310 else
2311 {
2312 _ASSERTE(_AncestorTask._GetImpl()->_IsCanceled());
2313 if (_AncestorTask._GetImpl()->_HasUserException())
2314 {
2315 // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of
2316 // _UnwrappedTask. Instead, it is the enclosing task.
2317 _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false);
2318 }
2319 else
2320 {
2321 _OuterTask->_Cancel(true);
2322 }
2323 }
2324 },
2325 nullptr,
2326 details::_DefaultAutoInline);
2327 }
2328
2329 scheduler_ptr _GetScheduler() const { return _M_TaskCollection._GetScheduler(); }
2330
2331 // Tracks the internal state of the task
2332 std::atomic<_TaskInternalState> _M_TaskState;
2333 // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this
2334 // task returns an async operation or async action that is unwrapped by the runtime.
2335 bool _M_fFromAsync;
2336 // Set to true when a continuation unwraps a task or async operation.
2337 bool _M_fUnwrappedTask;
2338
2339 // An exception thrown by the task body is captured in an exception holder and it is shared with all value based
2340 // continuations rooted at the task. The exception is 'observed' if the user invokes get()/wait() on any of the
2341 // tasks that are sharing this exception holder. If the exception is not observed by the time the internal object
2342 // owned by the shared pointer destructs, the process will fail fast.
2343 std::shared_ptr<_ExceptionHolder> _M_exceptionHolder;
2344
2345 ::pplx::extensibility::critical_section_t _M_ContinuationsCritSec;
2346
2347 // The cancellation token state.
2348 _CancellationTokenState* _M_pTokenState;
2349
2350 // The registration on the token.
2351 _CancellationTokenRegistration* _M_pRegistration;
2352
2353 typedef _ContinuationTaskHandleBase* _ContinuationList;
2354 _ContinuationList _M_Continuations;
2355
2356 // The async task collection wrapper
2357 ::pplx::details::_TaskCollection_t _M_TaskCollection;
2358
2359 // Callstack for function call (constructor or .then) that created this task impl.
2360 _TaskCreationCallstack _M_pTaskCreationCallstack;
2361
2362 _TaskEventLogger _M_taskEventLogger;
2363
2364 private:
2365 // Must not be copied by value:
2366 _Task_impl_base(const _Task_impl_base&);
2367 _Task_impl_base const& operator=(_Task_impl_base const&);
2368 };
2369
2370 #if PPLX_TASK_ASYNC_LOGGING
2371 inline void _TaskEventLogger::_LogTaskCompleted()
2372 {
2373 if (_M_scheduled)
2374 {
2375 ::Windows::Foundation::AsyncStatus _State;
2376 if (_M_task->_IsCompleted())
2377 _State = ::Windows::Foundation::AsyncStatus::Completed;
2378 else if (_M_task->_HasUserException())
2379 _State = ::Windows::Foundation::AsyncStatus::Error;
2380 else
2381 _State = ::Windows::Foundation::AsyncStatus::Canceled;
2382
2383 if (details::_IsCausalitySupported())
2384 {
2385 ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(
2386 ::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required,
2387 ::Windows::Foundation::Diagnostics::CausalitySource::Library,
2388 _PPLTaskCausalityPlatformID,
2389 reinterpret_cast<unsigned long long>(_M_task),
2390 _State);
2391 }
2392 }
2393 }
2394 #endif
2395
2396 /// <summary>
2397 /// The implementation of a first-class task. This structure contains the task group used to execute
2398 /// the task function and handles the scheduling. The _Task_impl is created as a shared_ptr
2399 /// member of the the public task class, so its destruction is handled automatically.
2400 /// </summary>
2401 /// <typeparam name="_ReturnType">
2402 /// The result type of this task.
2403 /// </typeparam>
2404 /**/
2405 template<typename _ReturnType>
2406 struct _Task_impl : public _Task_impl_base
2407 {
2408 #if defined(__cplusplus_winrt)
2409 typedef Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value>
2410 _AsyncOperationType;
2411 #endif // defined(__cplusplus_winrt)
2412 _Task_impl(_CancellationTokenState* _Ct, scheduler_ptr _Scheduler_arg) : _Task_impl_base(_Ct, _Scheduler_arg)
2413 {
2414 #if defined(__cplusplus_winrt)
2415 _M_unwrapped_async_op = nullptr;
2416 #endif /* defined (__cplusplus_winrt) */
2417 }
2418
2419 virtual ~_Task_impl()
2420 {
2421 // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class
2422 // destructor could cause a partially initialized _Task_impl to be in the list of registrations for a
2423 // cancellation token.
2424 _DeregisterCancellation();
2425 }
2426
2427 virtual bool _CancelAndRunContinuations(bool _SynchronousCancel,
2428 bool _UserException,
2429 bool _PropagatedFromAncestor,
2430 const std::shared_ptr<_ExceptionHolder>& _ExceptionHolder_arg)
2431 {
2432 bool _RunContinuations = false;
2433 {
2434 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);
2435 if (_UserException)
2436 {
2437 _ASSERTE(_SynchronousCancel && !_IsCompleted());
2438 // If the state is _Canceled, the exception has to be coming from an ancestor.
2439 _ASSERTE(!_IsCanceled() || _PropagatedFromAncestor);
2440
2441 // We should not be canceled with an exception more than once.
2442 _ASSERTE(!_HasUserException());
2443
2444 // Mark _PropagatedFromAncestor as used.
2445 (void)_PropagatedFromAncestor;
2446
2447 if (_M_TaskState == _Canceled)
2448 {
2449 // If the task has finished canceling there should not be any continuation records in the array.
2450 return false;
2451 }
2452 else
2453 {
2454 _ASSERTE(_M_TaskState != _Completed);
2455 _M_exceptionHolder = _ExceptionHolder_arg;
2456 }
2457 }
2458 else
2459 {
2460 // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do
2461 // better than the last async cancel which is to say, cancellation is already initiated, so return
2462 // early.
2463 if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel))
2464 {
2465 _ASSERTE(!_IsCompleted() || !_HasUserException());
2466 return false;
2467 }
2468 _ASSERTE(!_SynchronousCancel || !_HasUserException());
2469 }
2470
2471 if (_SynchronousCancel)
2472 {
2473 // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this
2474 // and wait()
2475 _M_TaskState = _Canceled;
2476 // Cancellation completes the task, so all dependent tasks must be run to cancel them
2477 // They are canceled when they begin running (see _RunContinuation) and see that their
2478 // ancestor has been canceled.
2479 _RunContinuations = true;
2480 }
2481 else
2482 {
2483 _ASSERTE(!_UserException);
2484
2485 if (_IsStarted())
2486 {
2487 #if defined(__cplusplus_winrt)
2488 if (_M_unwrapped_async_op != nullptr)
2489 {
2490 // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks
2491 // cannot be canceled without its token.
2492 _M_unwrapped_async_op->Cancel();
2493 }
2494 #endif /* defined (__cplusplus_winrt) */
2495 _M_TaskCollection._Cancel();
2496 }
2497
2498 // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not
2499 // executing user code anymore). In the case of a synchronous cancel, this can happen immediately,
2500 // whereas with an asynchronous cancel, the task has to move from _Started to _PendingCancel before it
2501 // can move to _Canceled when it is finished executing.
2502 _M_TaskState = _PendingCancel;
2503
2504 _M_taskEventLogger._LogCancelTask();
2505 }
2506 }
2507
2508 // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled
2509 // state.
2510 if (_RunContinuations)
2511 {
2512 _M_TaskCollection._Complete();
2513
2514 if (_M_Continuations)
2515 {
2516 // Scheduling cancellation with automatic inlining.
2517 _ScheduleFuncWithAutoInline([=]() { _RunTaskContinuations(); }, details::_DefaultAutoInline);
2518 }
2519 }
2520 return true;
2521 }
2522
2523 void _FinalizeAndRunContinuations(_ReturnType _Result)
2524 {
2525 _M_Result.Set(_Result);
2526
2527 {
2528 //
2529 // Hold this lock to ensure continuations being concurrently either get added
2530 // to the _M_Continuations vector or wait for the result
2531 //
2532 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);
2533
2534 // A task could still be in the _Created state if it was created with a task_completion_event.
2535 // It could also be in the _Canceled state for the same reason.
2536 _ASSERTE(!_HasUserException() && !_IsCompleted());
2537 if (_IsCanceled())
2538 {
2539 return;
2540 }
2541
2542 // Always transition to "completed" state, even in the face of unacknowledged pending cancellation
2543 _M_TaskState = _Completed;
2544 }
2545 _M_TaskCollection._Complete();
2546 _RunTaskContinuations();
2547 }
2548
2549 //
2550 // This method is invoked when the starts executing. The task returns early if this method returns true.
2551 //
2552 bool _TransitionedToStarted()
2553 {
2554 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);
2555 // Canceled state could only result from antecedent task's canceled state, but that code path will not reach
2556 // here.
2557 _ASSERTE(!_IsCanceled());
2558 if (_IsPendingCancel()) return false;
2559
2560 _ASSERTE(_IsCreated());
2561 _M_TaskState = _Started;
2562 return true;
2563 }
2564
2565 #if defined(__cplusplus_winrt)
2566 void _SetUnwrappedAsyncOp(_AsyncOperationType ^ _AsyncOp)
2567 {
2568 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_ContinuationsCritSec);
2569 // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it.
2570 if (_IsPendingCancel())
2571 {
2572 _ASSERTE(!_IsCanceled());
2573 _AsyncOp->Cancel();
2574 }
2575 else
2576 {
2577 _M_unwrapped_async_op = _AsyncOp;
2578 }
2579 }
2580 #endif /* defined (__cplusplus_winrt) */
2581
2582 // Return true if the task has reached a terminal state
2583 bool _IsDone() { return _IsCompleted() || _IsCanceled(); }
2584
2585 _ReturnType _GetResult() { return _M_Result.Get(); }
2586
2587 _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor.
2588 #if defined(__cplusplus_winrt)
2589 _AsyncOperationType ^ _M_unwrapped_async_op;
2590 #endif /* defined (__cplusplus_winrt) */
2591 };
2592
2593 template<typename _ResultType>
2594 struct _Task_completion_event_impl
2595 {
2596 private:
2597 _Task_completion_event_impl(const _Task_completion_event_impl&);
2598 _Task_completion_event_impl& operator=(const _Task_completion_event_impl&);
2599
2600 public:
2601 typedef std::vector<typename _Task_ptr<_ResultType>::_Type> _TaskList;
2602
2603 _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) {}
2604
2605 bool _HasUserException() { return _M_exceptionHolder != nullptr; }
2606
2607 ~_Task_completion_event_impl()
2608 {
2609 for (auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt)
2610 {
2611 _ASSERTE(!_M_fHasValue && !_M_fIsCanceled);
2612 // Cancel the tasks since the event was never signaled or canceled.
2613 (*_TaskIt)->_Cancel(true);
2614 }
2615 }
2616
2617 // We need to protect the loop over the array, so concurrent_vector would not have helped
2618 _TaskList _M_tasks;
2619 ::pplx::extensibility::critical_section_t _M_taskListCritSec;
2620 _ResultHolder<_ResultType> _M_value;
2621 std::shared_ptr<_ExceptionHolder> _M_exceptionHolder;
2622 std::atomic<bool> _M_fHasValue;
2623 std::atomic<bool> _M_fIsCanceled;
2624 };
2625
2626 // Utility method for dealing with void functions
2627 inline std::function<_Unit_type(void)> _MakeVoidToUnitFunc(const std::function<void(void)>& _Func)
2628 {
2629 return [=]() -> _Unit_type {
2630 _Func();
2631 return _Unit_type();
2632 };
2633 }
2634
2635 template<typename _Type>
2636 std::function<_Type(_Unit_type)> _MakeUnitToTFunc(const std::function<_Type(void)>& _Func)
2637 {
2638 return [=](_Unit_type) -> _Type { return _Func(); };
2639 }
2640
2641 template<typename _Type>
2642 std::function<_Unit_type(_Type)> _MakeTToUnitFunc(const std::function<void(_Type)>& _Func)
2643 {
2644 return [=](_Type t) -> _Unit_type {
2645 _Func(t);
2646 return _Unit_type();
2647 };
2648 }
2649
2650 inline std::function<_Unit_type(_Unit_type)> _MakeUnitToUnitFunc(const std::function<void(void)>& _Func)
2651 {
2652 return [=](_Unit_type) -> _Unit_type {
2653 _Func();
2654 return _Unit_type();
2655 };
2656 }
2657 } // namespace details
2658
2659 /// <summary>
2660 /// The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is
2661 /// satisfied, or start a task in response to an external event.
2662 /// </summary>
2663 /// <typeparam name="_ResultType">
2664 /// The result type of this <c>task_completion_event</c> class.
2665 /// </typeparam>
2666 /// <remarks>
2667 /// Use a task created from a task completion event when your scenario requires you to create a task that will
2668 /// complete, and thereby have its continuations scheduled for execution, at some point in the future. The
2669 /// <c>task_completion_event</c> must have the same type as the task you create, and calling the set method on the
2670 /// task completion event with a value of that type will cause the associated task to complete, and provide that
2671 /// value as a result to its continuations. <para>If the task completion event is never signaled, any tasks created
2672 /// from it will be canceled when it is destructed.</para> <para><c>task_completion_event</c> behaves like a smart
2673 /// pointer, and should be passed by value.</para>
2674 /// </remarks>
2675 /// <seealso cref="task Class"/>
2676 /**/
2677 template<typename _ResultType>
2678 class task_completion_event
2679 {
2680 public:
2681 /// <summary>
2682 /// Constructs a <c>task_completion_event</c> object.
2683 /// </summary>
2684 /**/
2685 task_completion_event() : _M_Impl(std::make_shared<details::_Task_completion_event_impl<_ResultType>>()) {}
2686
2687 /// <summary>
2688 /// Sets the task completion event.
2689 /// </summary>
2690 /// <param name="_Result">
2691 /// The result to set this event with.
2692 /// </param>
2693 /// <returns>
2694 /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the
2695 /// event is already set.
2696 /// </returns>
2697 /// <remarks>
2698 /// In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its
2699 /// result (if any) will be stored in the task completion event. The remaining sets are ignored and the method
2700 /// will return false. When you set a task completion event, all the tasks created from that event will
2701 /// immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have a
2702 /// <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to
2703 /// their continuations.
2704 /// </remarks>
2705 /**/
2706 bool set(_ResultType _Result)
2707 const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2708 {
2709 // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are
2710 // ignored.
2711 if (_IsTriggered())
2712 {
2713 return false;
2714 }
2715
2716 _TaskList _Tasks;
2717 bool _RunContinuations = false;
2718 {
2719 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);
2720
2721 if (!_IsTriggered())
2722 {
2723 _M_Impl->_M_value.Set(_Result);
2724 _M_Impl->_M_fHasValue = true;
2725
2726 _Tasks.swap(_M_Impl->_M_tasks);
2727 _RunContinuations = true;
2728 }
2729 }
2730
2731 if (_RunContinuations)
2732 {
2733 for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt)
2734 {
2735 // If current task was canceled by a cancellation_token, it would be in cancel pending state.
2736 if ((*_TaskIt)->_IsPendingCancel())
2737 (*_TaskIt)->_Cancel(true);
2738 else
2739 {
2740 // Tasks created with task_completion_events can be marked as async, (we do this in when_any and
2741 // when_all if one of the tasks involved is an async task). Since continuations of async tasks can
2742 // execute inline, we need to run continuations after the lock is released.
2743 (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get());
2744 }
2745 }
2746 if (_M_Impl->_HasUserException())
2747 {
2748 _M_Impl->_M_exceptionHolder.reset();
2749 }
2750 return true;
2751 }
2752
2753 return false;
2754 }
2755
2756 template<typename _E>
2757 __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result
2758 bool set_exception(
2759 _E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2760 {
2761 // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for
2762 // set_exception.
2763 return _Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK());
2764 }
2765
2766 /// <summary>
2767 /// Propagates an exception to all tasks associated with this event.
2768 /// </summary>
2769 /// <param>
2770 /// The exception_ptr that indicates the exception to set this event with.
2771 /// </param>
2772 /**/
2773 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
2774 bool set_exception(std::exception_ptr _ExceptionPtr)
2775 const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2776 {
2777 // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for
2778 // set_exception.
2779 return _Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK());
2780 }
2781
2782 /// <summary>
2783 /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as
2784 /// canceled if it has not already been set.
2785 /// </summary>
2786 bool _Cancel() const
2787 {
2788 // Cancel with the stored exception if one exists.
2789 return _CancelInternal();
2790 }
2791
2792 /// <summary>
2793 /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this
2794 /// event will be canceled with the same exception.
2795 /// </summary>
2796 template<typename _ExHolderType>
2797 bool _Cancel(
2798 _ExHolderType _ExHolder,
2799 const details::_TaskCreationCallstack& _SetExceptionAddressHint = details::_TaskCreationCallstack()) const
2800 {
2801 bool _Canceled;
2802 if (_StoreException(_ExHolder, _SetExceptionAddressHint))
2803 {
2804 _Canceled = _CancelInternal();
2805 _ASSERTE(_Canceled);
2806 }
2807 else
2808 {
2809 _Canceled = false;
2810 }
2811 return _Canceled;
2812 }
2813
2814 /// <summary>
2815 /// Internal method that stores an exception in the task completion event. This is used internally by when_any.
2816 /// Note, this does not cancel the task completion event. A task completion event with a stored exception
2817 /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present.
2818 /// </summary>
2819 template<typename _ExHolderType>
2820 bool _StoreException(
2821 _ExHolderType _ExHolder,
2822 const details::_TaskCreationCallstack& _SetExceptionAddressHint = details::_TaskCreationCallstack()) const
2823 {
2824 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);
2825 if (!_IsTriggered() && !_M_Impl->_HasUserException())
2826 {
2827 // Create the exception holder only if we have ensured there we will be successful in setting it onto the
2828 // task completion event. Failing to do so will result in an unobserved task exception.
2829 _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint);
2830 return true;
2831 }
2832 return false;
2833 }
2834
2835 /// <summary>
2836 /// Tests whether current event has been either Set, or Canceled.
2837 /// </summary>
2838 bool _IsTriggered() const { return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; }
2839
2840 private:
2841 static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(
2842 const std::shared_ptr<details::_ExceptionHolder>& _ExHolder, const details::_TaskCreationCallstack&)
2843 {
2844 return _ExHolder;
2845 }
2846
2847 static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(
2848 std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack& _SetExceptionAddressHint)
2849 {
2850 return std::make_shared<details::_ExceptionHolder>(_ExceptionPtr, _SetExceptionAddressHint);
2851 }
2852
2853 template<typename T>
2854 friend class task; // task can register itself with the event by calling the private _RegisterTask
2855 template<typename T>
2856 friend class task_completion_event;
2857
2858 typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList;
2859
2860 /// <summary>
2861 /// Cancels the task_completion_event.
2862 /// </summary>
2863 bool _CancelInternal() const
2864 {
2865 // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal
2866 // will never be invoked if the task completion event has been set.
2867 _ASSERTE(!_M_Impl->_M_fHasValue);
2868 if (_M_Impl->_M_fIsCanceled)
2869 {
2870 return false;
2871 }
2872
2873 _TaskList _Tasks;
2874 bool _Cancel = false;
2875 {
2876 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);
2877 _ASSERTE(!_M_Impl->_M_fHasValue);
2878 if (!_M_Impl->_M_fIsCanceled)
2879 {
2880 _M_Impl->_M_fIsCanceled = true;
2881 _Tasks.swap(_M_Impl->_M_tasks);
2882 _Cancel = true;
2883 }
2884 }
2885
2886 bool _UserException = _M_Impl->_HasUserException();
2887
2888 if (_Cancel)
2889 {
2890 for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt)
2891 {
2892 // Need to call this after the lock is released. See comments in set().
2893 if (_UserException)
2894 {
2895 (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true);
2896 }
2897 else
2898 {
2899 (*_TaskIt)->_Cancel(true);
2900 }
2901 }
2902 }
2903 return _Cancel;
2904 }
2905
2906 /// <summary>
2907 /// Register a task with this event. This function is called when a task is constructed using
2908 /// a task_completion_event.
2909 /// </summary>
2910 void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type& _TaskParam)
2911 {
2912 ::pplx::extensibility::scoped_critical_section_t _LockHolder(_M_Impl->_M_taskListCritSec);
2913
2914 // If an exception was already set on this event, then cancel the task with the stored exception.
2915 if (_M_Impl->_HasUserException())
2916 {
2917 _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true);
2918 }
2919 else if (_M_Impl->_M_fHasValue)
2920 {
2921 _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get());
2922 }
2923 else
2924 {
2925 _M_Impl->_M_tasks.push_back(_TaskParam);
2926 }
2927 }
2928
2929 std::shared_ptr<details::_Task_completion_event_impl<_ResultType>> _M_Impl;
2930 };
2931
2932 /// <summary>
2933 /// The <c>task_completion_event</c> class allows you to delay the execution of a task until a condition is
2934 /// satisfied, or start a task in response to an external event.
2935 /// </summary>
2936 /// <remarks>
2937 /// Use a task created from a task completion event when your scenario requires you to create a task that will
2938 /// complete, and thereby have its continuations scheduled for execution, at some point in the future. The
2939 /// <c>task_completion_event</c> must have the same type as the task you create, and calling the set method on the
2940 /// task completion event with a value of that type will cause the associated task to complete, and provide that
2941 /// value as a result to its continuations. <para>If the task completion event is never signaled, any tasks created
2942 /// from it will be canceled when it is destructed.</para> <para><c>task_completion_event</c> behaves like a smart
2943 /// pointer, and should be passed by value.</para>
2944 /// </remarks>
2945 /// <seealso cref="task Class"/>
2946 /**/
2947 template<>
2948 class task_completion_event<void>
2949 {
2950 public:
2951 /// <summary>
2952 /// Sets the task completion event.
2953 /// </summary>
2954 /// <returns>
2955 /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the
2956 /// event is already set.
2957 /// </returns>
2958 /// <remarks>
2959 /// In the presence of multiple or concurrent calls to <c>set</c>, only the first call will succeed and its
2960 /// result (if any) will be stored in the task completion event. The remaining sets are ignored and the method
2961 /// will return false. When you set a task completion event, all the tasks created from that event will
2962 /// immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have a
2963 /// <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to
2964 /// their continuations.
2965 /// </remarks>
2966 /**/
2967 bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2968 {
2969 return _M_unitEvent.set(details::_Unit_type());
2970 }
2971
2972 template<typename _E>
2973 __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result
2974 bool set_exception(
2975 _E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2976 {
2977 return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), PPLX_CAPTURE_CALLSTACK());
2978 }
2979
2980 /// <summary>
2981 /// Propagates an exception to all tasks associated with this event.
2982 /// </summary>
2983 /// <param>
2984 /// The exception_ptr that indicates the exception to set this event with.
2985 /// </param>
2986 /**/
2987 __declspec(
2988 noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK intrinsic gives us the expected result
2989 bool set_exception(std::exception_ptr _ExceptionPtr)
2990 const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
2991 {
2992 // It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for
2993 // set_exception.
2994 return _M_unitEvent._Cancel(_ExceptionPtr, PPLX_CAPTURE_CALLSTACK());
2995 }
2996
2997 /// <summary>
2998 /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has
2999 /// not already been set.
3000 /// </summary>
3001 void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
3002 {
3003 _M_unitEvent._Cancel();
3004 }
3005
3006 /// <summary>
3007 /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will
3008 /// be canceled with the same exception.
3009 /// </summary>
3010 void _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const { _M_unitEvent._Cancel(_ExHolder); }
3011
3012 /// <summary>
3013 /// Method that stores an exception in the task completion event. This is used internally by when_any.
3014 /// Note, this does not cancel the task completion event. A task completion event with a stored exception
3015 /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present.
3016 /// </summary>
3017 bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const
3018 {
3019 return _M_unitEvent._StoreException(_ExHolder);
3020 }
3021
3022 /// <summary>
3023 /// Test whether current event has been either Set, or Canceled.
3024 /// </summary>
3025 bool _IsTriggered() const { return _M_unitEvent._IsTriggered(); }
3026
3027 private:
3028 template<typename T>
3029 friend class task; // task can register itself with the event by calling the private _RegisterTask
3030
3031 /// <summary>
3032 /// Register a task with this event. This function is called when a task is constructed using
3033 /// a task_completion_event.
3034 /// </summary>
3035 void _RegisterTask(details::_Task_ptr<details::_Unit_type>::_Type _TaskParam)
3036 {
3037 _M_unitEvent._RegisterTask(_TaskParam);
3038 }
3039
3040 // The void event contains an event a dummy type so common code can be used for events with void and non-void
3041 // results.
3042 task_completion_event<details::_Unit_type> _M_unitEvent;
3043 };
3044
3045 namespace details
3046 {
3047 //
3048 // Compile-time validation helpers
3049 //
3050
3051 // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here.
3052 //
3053 // Anything callable is fine
3054 template<typename _ReturnType, typename _Ty>
3055 auto _IsValidTaskCtor(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type());
3056
3057 #if defined(__cplusplus_winrt)
3058 // Anything that has GetResults is fine: this covers all async operations
3059 template<typename _ReturnType, typename _Ty>
3060 auto _IsValidTaskCtor(_Ty _Param, int, int, int, ...) -> decltype(_Param->GetResults(), std::true_type());
3061 #endif
3062
3063 // Allow parameters with set: this covers task_completion_event
3064 template<typename _ReturnType, typename _Ty>
3065 auto _IsValidTaskCtor(_Ty _Param, int, int, ...)
3066 -> decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type());
3067
3068 template<typename _ReturnType, typename _Ty>
3069 auto _IsValidTaskCtor(_Ty _Param, int, ...) -> decltype(_Param.set(), std::true_type());
3070
3071 // All else is invalid
3072 template<typename _ReturnType, typename _Ty>
3073 std::false_type _IsValidTaskCtor(_Ty _Param, ...);
3074
3075 template<typename _ReturnType, typename _Ty>
3076 void _ValidateTaskConstructorArgs(_Ty _Param)
3077 {
3078 static_assert(std::is_same<decltype(_IsValidTaskCtor<_ReturnType>(_Param, 0, 0, 0, 0)), std::true_type>::value,
3079 #if defined(__cplusplus_winrt)
3080 "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a "
3081 "task_completion_event"
3082 #else /* defined (__cplusplus_winrt) */
3083 "incorrect argument for task constructor; can be a callable object or a task_completion_event"
3084 #endif /* defined (__cplusplus_winrt) */
3085 );
3086 #if defined(__cplusplus_winrt)
3087 static_assert(!(std::is_same<_Ty, _ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value),
3088 "incorrect template argument for task; consider using the return type of the async operation");
3089 #endif /* defined (__cplusplus_winrt) */
3090 }
3091
3092 #if defined(__cplusplus_winrt)
3093 // Helpers for create_async validation
3094 //
3095 // A parameter lambda taking no arguments is valid
3096 template<typename _Ty>
3097 static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int) -> decltype(_Param(), std::true_type());
3098
3099 // A parameter lambda taking an cancellation_token argument is valid
3100 template<typename _Ty>
3101 static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...)
3102 -> decltype(_Param(cancellation_token::none()), std::true_type());
3103
3104 // A parameter lambda taking a progress report argument is valid
3105 template<typename _Ty>
3106 static auto _IsValidCreateAsync(_Ty _Param, int, int, ...)
3107 -> decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type());
3108
3109 // A parameter lambda taking a progress report and a cancellation_token argument is valid
3110 template<typename _Ty>
3111 static auto _IsValidCreateAsync(_Ty _Param, int, ...)
3112 -> decltype(_Param(details::_ProgressReporterCtorArgType(), cancellation_token::none()), std::true_type());
3113
3114 // All else is invalid
3115 template<typename _Ty>
3116 static std::false_type _IsValidCreateAsync(_Ty _Param, ...);
3117 #endif /* defined (__cplusplus_winrt) */
3118
3119 /// <summary>
3120 /// A helper class template that makes only movable functions be able to be passed to std::function
3121 /// </summary>
3122 template<typename _Ty>
3123 struct _NonCopyableFunctorWrapper
3124 {
3125 template<typename _Tx,
3126 typename = typename std::enable_if<
3127 !std::is_base_of<_NonCopyableFunctorWrapper<_Ty>, typename std::decay<_Tx>::type>::value>::type>
3128 explicit _NonCopyableFunctorWrapper(_Tx&& f) : _M_functor {std::make_shared<_Ty>(std::forward<_Tx>(f))}
3129 {
3130 }
3131
3132 template<class... _Args>
3133 auto operator()(_Args&&... args) -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))
3134 {
3135 return _M_functor->operator()(std::forward<_Args>(args)...);
3136 }
3137
3138 template<class... _Args>
3139 auto operator()(_Args&&... args) const -> decltype(std::declval<_Ty>()(std::forward<_Args>(args)...))
3140 {
3141 return _M_functor->operator()(std::forward<_Args>(args)...);
3142 }
3143
3144 std::shared_ptr<_Ty> _M_functor;
3145 };
3146
3147 template<typename _Ty, typename Enable = void>
3148 struct _CopyableFunctor
3149 {
3150 typedef _Ty _Type;
3151 };
3152
3153 template<typename _Ty>
3154 struct _CopyableFunctor<
3155 _Ty,
3156 typename std::enable_if<std::is_move_constructible<_Ty>::value && !std::is_copy_constructible<_Ty>::value>::type>
3157 {
3158 typedef _NonCopyableFunctorWrapper<_Ty> _Type;
3159 };
3160 } // namespace details
3161 /// <summary>
3162 /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a
3163 /// lambda that takes and returns a non-void type (details::_Unit_type is used to substitute for void). This is to
3164 /// minimize the special handling required for 'void'.
3165 /// </summary>
3166 template<typename _InpType, typename _OutType>
3167 class _Continuation_func_transformer
3168 {
3169 public:
3170 static auto _Perform(std::function<_OutType(_InpType)> _Func) -> decltype(_Func) { return _Func; }
3171 };
3172
3173 template<typename _OutType>
3174 class _Continuation_func_transformer<void, _OutType>
3175 {
3176 public:
3177 static auto _Perform(std::function<_OutType(void)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func))
3178 {
3179 return details::_MakeUnitToTFunc<_OutType>(_Func);
3180 }
3181 };
3182
3183 template<typename _InType>
3184 class _Continuation_func_transformer<_InType, void>
3185 {
3186 public:
3187 static auto _Perform(std::function<void(_InType)> _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func))
3188 {
3189 return details::_MakeTToUnitFunc<_InType>(_Func);
3190 }
3191 };
3192
3193 template<>
3194 class _Continuation_func_transformer<void, void>
3195 {
3196 public:
3197 static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func))
3198 {
3199 return details::_MakeUnitToUnitFunc(_Func);
3200 }
3201 };
3202
3203 // A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type
3204 // (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'.
3205 template<typename _RetType>
3206 class _Init_func_transformer
3207 {
3208 public:
3209 static auto _Perform(std::function<_RetType(void)> _Func) -> decltype(_Func) { return _Func; }
3210 };
3211
3212 template<>
3213 class _Init_func_transformer<void>
3214 {
3215 public:
3216 static auto _Perform(std::function<void(void)> _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func))
3217 {
3218 return details::_MakeVoidToUnitFunc(_Func);
3219 }
3220 };
3221
3222 /// <summary>
3223 /// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed
3224 /// asynchronously, and concurrently with other tasks and parallel work produced by parallel algorithms in the
3225 /// Concurrency Runtime. It produces a result of type <typeparamref name="_ResultType"/> on successful completion.
3226 /// Tasks of type <c>task<void></c> produce no result. A task can be waited upon and canceled independently of
3227 /// other tasks. It can also be composed with other tasks using continuations(<c>then</c>), and
3228 /// join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.
3229 /// </summary>
3230 /// <typeparam name="_ReturnType">
3231 /// The result type of this task.
3232 /// </typeparam>
3233 /// <remarks>
3234 /// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.
3235 /// </remarks>
3236 /**/
3237 template<typename _ReturnType>
3238 class task
3239 {
3240 public:
3241 /// <summary>
3242 /// The type of the result an object of this class produces.
3243 /// </summary>
3244 /**/
3245 typedef _ReturnType result_type;
3246
3247 /// <summary>
3248 /// Constructs a <c>task</c> object.
3249 /// </summary>
3250 /// <remarks>
3251 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
3252 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
3253 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
3254 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
3255 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
3256 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
3257 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
3258 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
3259 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
3260 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
3261 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
3262 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
3263 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
3264 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
3265 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
3266 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
3267 /// Runtime)"/>.</para>
3268 /// </remarks>
3269 /**/
3270 task() : _M_Impl(nullptr)
3271 {
3272 // The default constructor should create a task with a nullptr impl. This is a signal that the
3273 // task is not usable and should throw if any wait(), get() or then() APIs are used.
3274 }
3275
3276 /// <summary>
3277 /// Constructs a <c>task</c> object.
3278 /// </summary>
3279 /// <typeparam name="_Ty">
3280 /// The type of the parameter from which the task is to be constructed.
3281 /// </typeparam>
3282 /// <param name="_Param">
3283 /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a
3284 /// <c>task_completion_event<result_type></c> object, or a Windows::Foundation::IAsyncInfo if you are
3285 /// using tasks in your Windows Store app. The lambda or function object should be a type equivalent to
3286 /// <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
3287 /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
3288 /// </param>
3289 /// <param name="_Token">
3290 /// The cancellation token to associate with this task. A task created without a cancellation token cannot be
3291 /// canceled. It implicitly receives the token <c>cancellation_token::none()</c>.
3292 /// </param>
3293 /// <remarks>
3294 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
3295 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
3296 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
3297 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
3298 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
3299 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
3300 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
3301 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
3302 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
3303 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
3304 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
3305 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
3306 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
3307 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
3308 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
3309 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
3310 /// Runtime)"/>.</para>
3311 /// </remarks>
3312 /**/
3313 template<typename _Ty>
3314 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
3315 explicit task(_Ty _Param)
3316 {
3317 task_options _TaskOptions;
3318 details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param);
3319
3320 _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());
3321 // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the
3322 // the call site of the task constructor.
3323 _SetTaskCreationCallstack(PPLX_CAPTURE_CALLSTACK());
3324
3325 _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));
3326 }
3327
3328 /// <summary>
3329 /// Constructs a <c>task</c> object.
3330 /// </summary>
3331 /// <typeparam name="_Ty">
3332 /// The type of the parameter from which the task is to be constructed.
3333 /// </typeparam>
3334 /// <param name="_Param">
3335 /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a
3336 /// <c>task_completion_event<result_type></c> object, or a Windows::Foundation::IAsyncInfo if you are
3337 /// using tasks in your Windows Store app. The lambda or function object should be a type equivalent to
3338 /// <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
3339 /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
3340 /// </param>
3341 /// <param name="_TaskOptions">
3342 /// The task options include cancellation token, scheduler etc
3343 /// </param>
3344 /// <remarks>
3345 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
3346 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
3347 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
3348 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
3349 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
3350 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
3351 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
3352 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
3353 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
3354 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
3355 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
3356 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
3357 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
3358 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
3359 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
3360 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
3361 /// Runtime)"/>.</para>
3362 /// </remarks>
3363 /**/
3364 template<typename _Ty>
3365 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
3366 explicit task(_Ty _Param, const task_options& _TaskOptions)
3367 {
3368 details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param);
3369
3370 _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());
3371 // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the
3372 // the call site of the task constructor.
3373 _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack
3374 ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack
3375 : PPLX_CAPTURE_CALLSTACK());
3376
3377 _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));
3378 }
3379
3380 /// <summary>
3381 /// Constructs a <c>task</c> object.
3382 /// </summary>
3383 /// <param name="_Other">
3384 /// The source <c>task</c> object.
3385 /// </param>
3386 /// <remarks>
3387 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
3388 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
3389 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
3390 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
3391 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
3392 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
3393 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
3394 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
3395 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
3396 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
3397 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
3398 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
3399 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
3400 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
3401 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
3402 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
3403 /// Runtime)"/>.</para>
3404 /// </remarks>
3405 /**/
3406 task(const task& _Other) : _M_Impl(_Other._M_Impl) {}
3407
3408 /// <summary>
3409 /// Constructs a <c>task</c> object.
3410 /// </summary>
3411 /// <param name="_Other">
3412 /// The source <c>task</c> object.
3413 /// </param>
3414 /// <remarks>
3415 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
3416 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
3417 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
3418 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
3419 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
3420 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
3421 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
3422 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
3423 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
3424 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
3425 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
3426 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
3427 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
3428 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
3429 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
3430 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
3431 /// Runtime)"/>.</para>
3432 /// </remarks>
3433 /**/
3434 task(task&& _Other) : _M_Impl(std::move(_Other._M_Impl)) {}
3435
3436 /// <summary>
3437 /// Replaces the contents of one <c>task</c> object with another.
3438 /// </summary>
3439 /// <param name="_Other">
3440 /// The source <c>task</c> object.
3441 /// </param>
3442 /// <remarks>
3443 /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents
3444 /// the same actual task as <paramref name="_Other"/> does.
3445 /// </remarks>
3446 /**/
3447 task& operator=(const task& _Other)
3448 {
3449 if (this != &_Other)
3450 {
3451 _M_Impl = _Other._M_Impl;
3452 }
3453 return *this;
3454 }
3455
3456 /// <summary>
3457 /// Replaces the contents of one <c>task</c> object with another.
3458 /// </summary>
3459 /// <param name="_Other">
3460 /// The source <c>task</c> object.
3461 /// </param>
3462 /// <remarks>
3463 /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents
3464 /// the same actual task as <paramref name="_Other"/> does.
3465 /// </remarks>
3466 /**/
3467 task& operator=(task&& _Other)
3468 {
3469 if (this != &_Other)
3470 {
3471 _M_Impl = std::move(_Other._M_Impl);
3472 }
3473 return *this;
3474 }
3475
3476 /// <summary>
3477 /// Adds a continuation task to this task.
3478 /// </summary>
3479 /// <typeparam name="_Function">
3480 /// The type of the function object that will be invoked by this task.
3481 /// </typeparam>
3482 /// <param name="_Func">
3483 /// The continuation function to execute when this task completes. This continuation function must take as input
3484 /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the
3485 /// type of the result this task produces.
3486 /// </param>
3487 /// <returns>
3488 /// The newly created continuation task. The result type of the returned task is determined by what <paramref
3489 /// name="_Func"/> returns.
3490 /// </returns>
3491 /// <remarks>
3492 /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo
3493 /// interface, are only available to Windows Store apps. <para>For more information on how to use task
3494 /// continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
3495 /// </remarks>
3496 /**/
3497 template<typename _Function>
3498 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
3499 auto then(_Function&& _Func) const ->
3500 typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3501 {
3502 task_options _TaskOptions;
3503 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
3504 return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
3505 }
3506
3507 /// <summary>
3508 /// Adds a continuation task to this task.
3509 /// </summary>
3510 /// <typeparam name="_Function">
3511 /// The type of the function object that will be invoked by this task.
3512 /// </typeparam>
3513 /// <param name="_Func">
3514 /// The continuation function to execute when this task completes. This continuation function must take as input
3515 /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the
3516 /// type of the result this task produces.
3517 /// </param>
3518 /// <param name="_TaskOptions">
3519 /// The task options include cancellation token, scheduler and continuation context. By default the former 3
3520 /// options are inherited from the antecedent task
3521 /// </param>
3522 /// <returns>
3523 /// The newly created continuation task. The result type of the returned task is determined by what <paramref
3524 /// name="_Func"/> returns.
3525 /// </returns>
3526 /// <remarks>
3527 /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo
3528 /// interface, are only available to Windows Store apps. <para>For more information on how to use task
3529 /// continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
3530 /// </remarks>
3531 /**/
3532 template<typename _Function>
3533 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
3534 auto then(_Function&& _Func, task_options _TaskOptions) const ->
3535 typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3536 {
3537 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
3538 return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
3539 }
3540
3541 /// <summary>
3542 /// Adds a continuation task to this task.
3543 /// </summary>
3544 /// <typeparam name="_Function">
3545 /// The type of the function object that will be invoked by this task.
3546 /// </typeparam>
3547 /// <param name="_Func">
3548 /// The continuation function to execute when this task completes. This continuation function must take as input
3549 /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the
3550 /// type of the result this task produces.
3551 /// </param>
3552 /// <param name="_CancellationToken">
3553 /// The cancellation token to associate with the continuation task. A continuation task that is created without
3554 /// a cancellation token will inherit the token of its antecedent task.
3555 /// </param>
3556 /// <param name="_ContinuationContext">
3557 /// A variable that specifies where the continuation should execute. This variable is only useful when used in a
3558 /// Windows Store style app. For more information, see <see cref="task_continuation_context
3559 /// Class">task_continuation_context</see>
3560 /// </param>
3561 /// <returns>
3562 /// The newly created continuation task. The result type of the returned task is determined by what <paramref
3563 /// name="_Func"/> returns.
3564 /// </returns>
3565 /// <remarks>
3566 /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo
3567 /// interface, are only available to Windows Store apps. <para>For more information on how to use task
3568 /// continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
3569 /// </remarks>
3570 /**/
3571 template<typename _Function>
3572 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
3573 auto then(_Function&& _Func,
3574 cancellation_token _CancellationToken,
3575 task_continuation_context _ContinuationContext) const ->
3576 typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3577 {
3578 task_options _TaskOptions(_CancellationToken, _ContinuationContext);
3579 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
3580 return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func), _TaskOptions);
3581 }
3582
3583 /// <summary>
3584 /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if
3585 /// all of the tasks dependencies are satisfied, and it has not already been picked up for execution by a
3586 /// background worker.
3587 /// </summary>
3588 /// <returns>
3589 /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task
3590 /// encountered an exception during execution, or an exception was propagated to it from an antecedent task,
3591 /// <c>wait</c> will throw that exception.
3592 /// </returns>
3593 /**/
3594 task_status wait() const
3595 {
3596 if (!_M_Impl)
3597 {
3598 throw invalid_operation("wait() cannot be called on a default constructed task.");
3599 }
3600
3601 return _M_Impl->_Wait();
3602 }
3603
3604 /// <summary>
3605 /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will
3606 /// wait for the task to finish. This method does not return a value when called on a task with a
3607 /// <c>result_type</c> of <c>void</c>.
3608 /// </summary>
3609 /// <returns>
3610 /// The result of the task.
3611 /// </returns>
3612 /// <remarks>
3613 /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled
3614 /// Class">task_canceled</see> exception. If the task encountered an different exception or an exception was
3615 /// propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.
3616 /// </remarks>
3617 /**/
3618 _ReturnType get() const
3619 {
3620 if (!_M_Impl)
3621 {
3622 throw invalid_operation("get() cannot be called on a default constructed task.");
3623 }
3624
3625 if (_M_Impl->_Wait() == canceled)
3626 {
3627 throw task_canceled();
3628 }
3629
3630 return _M_Impl->_GetResult();
3631 }
3632
3633 /// <summary>
3634 /// Determines if the task is completed.
3635 /// </summary>
3636 /// <returns>
3637 /// True if the task has completed, false otherwise.
3638 /// </returns>
3639 /// <remarks>
3640 /// The function returns true if the task is completed or canceled (with or without user exception).
3641 /// </remarks>
3642 bool is_done() const
3643 {
3644 if (!_M_Impl)
3645 {
3646 throw invalid_operation("is_done() cannot be called on a default constructed task.");
3647 }
3648
3649 return _M_Impl->_IsDone();
3650 }
3651
3652 /// <summary>
3653 /// Returns the scheduler for this task
3654 /// </summary>
3655 /// <returns>
3656 /// A pointer to the scheduler
3657 /// </returns>
3658 scheduler_ptr scheduler() const
3659 {
3660 if (!_M_Impl)
3661 {
3662 throw invalid_operation("scheduler() cannot be called on a default constructed task.");
3663 }
3664
3665 return _M_Impl->_GetScheduler();
3666 }
3667
3668 /// <summary>
3669 /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such
3670 /// a task.
3671 /// </summary>
3672 /// <returns>
3673 /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task,
3674 /// <c>false</c> otherwise.
3675 /// </returns>
3676 /**/
3677 bool is_apartment_aware() const
3678 {
3679 if (!_M_Impl)
3680 {
3681 throw invalid_operation("is_apartment_aware() cannot be called on a default constructed task.");
3682 }
3683 return _M_Impl->_IsApartmentAware();
3684 }
3685
3686 /// <summary>
3687 /// Determines whether two <c>task</c> objects represent the same internal task.
3688 /// </summary>
3689 /// <returns>
3690 /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.
3691 /// </returns>
3692 /**/
3693 bool operator==(const task<_ReturnType>& _Rhs) const { return (_M_Impl == _Rhs._M_Impl); }
3694
3695 /// <summary>
3696 /// Determines whether two <c>task</c> objects represent different internal tasks.
3697 /// </summary>
3698 /// <returns>
3699 /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.
3700 /// </returns>
3701 /**/
3702 bool operator!=(const task<_ReturnType>& _Rhs) const { return !operator==(_Rhs); }
3703
3704 /// <summary>
3705 /// Create an underlying task implementation.
3706 /// </summary>
3707 void _CreateImpl(details::_CancellationTokenState* _Ct, scheduler_ptr _Scheduler)
3708 {
3709 _ASSERTE(_Ct != nullptr);
3710 _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler);
3711 if (_Ct != details::_CancellationTokenState::_None())
3712 {
3713 _M_Impl->_RegisterCancellation(_M_Impl);
3714 }
3715 }
3716
3717 /// <summary>
3718 /// Return the underlying implementation for this task.
3719 /// </summary>
3720 const typename details::_Task_ptr<_ReturnType>::_Type& _GetImpl() const { return _M_Impl; }
3721
3722 /// <summary>
3723 /// Set the implementation of the task to be the supplied implementation.
3724 /// </summary>
3725 void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type& _Impl)
3726 {
3727 _ASSERTE(!_M_Impl);
3728 _M_Impl = _Impl;
3729 }
3730
3731 /// <summary>
3732 /// Set the implementation of the task to be the supplied implementation using a move instead of a copy.
3733 /// </summary>
3734 void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type&& _Impl)
3735 {
3736 _ASSERTE(!_M_Impl);
3737 _M_Impl = std::move(_Impl);
3738 }
3739
3740 /// <summary>
3741 /// Sets a property determining whether the task is apartment aware.
3742 /// </summary>
3743 void _SetAsync(bool _Async = true) { _GetImpl()->_SetAsync(_Async); }
3744
3745 /// <summary>
3746 /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then
3747 /// method.
3748 /// </summary>
3749 void _SetTaskCreationCallstack(const details::_TaskCreationCallstack& _callstack)
3750 {
3751 _GetImpl()->_SetTaskCreationCallstack(_callstack);
3752 }
3753
3754 /// <summary>
3755 /// An internal version of then that takes additional flags and always execute the continuation inline by
3756 /// default. When _ForceInline is set to false, continuations inlining will be limited to default
3757 /// _DefaultAutoInline. This function is Used for runtime internal continuations only.
3758 /// </summary>
3759 template<typename _Function>
3760 auto _Then(_Function&& _Func,
3761 details::_CancellationTokenState* _PTokenState,
3762 details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const ->
3763 typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType
3764 {
3765 // inherit from antecedent
3766 auto _Scheduler = _GetImpl()->_GetScheduler();
3767
3768 return _ThenImpl<_ReturnType, _Function>(std::forward<_Function>(_Func),
3769 _PTokenState,
3770 task_continuation_context::use_default(),
3771 _Scheduler,
3772 PPLX_CAPTURE_CALLSTACK(),
3773 _InliningMode);
3774 }
3775
3776 private:
3777 template<typename T>
3778 friend class task;
3779
3780 // The task handle type used to construct an 'initial task' - a task with no dependents.
3781 template<typename _InternalReturnType, typename _Function, typename _TypeSelection>
3782 struct _InitialTaskHandle
3783 : details::_PPLTaskHandle<_ReturnType,
3784 _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>,
3785 details::_UnrealizedChore_t>
3786 {
3787 _Function _M_function;
3788 _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type& _TaskImpl, const _Function& _func)
3789 : details::_PPLTaskHandle<_ReturnType,
3790 _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>,
3791 details::_UnrealizedChore_t>::_PPLTaskHandle(_TaskImpl)
3792 , _M_function(_func)
3793 {
3794 }
3795
3796 virtual ~_InitialTaskHandle() {}
3797
3798 template<typename _Func>
3799 auto _LogWorkItemAndInvokeUserLambda(_Func&& _func) const -> decltype(_func())
3800 {
3801 details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger);
3802 (void)_LogWorkItem;
3803 return _func();
3804 }
3805
3806 void _Perform() const { _Init(_TypeSelection()); }
3807
3808 void _SyncCancelAndPropagateException() const { this->_M_pTask->_Cancel(true); }
3809
3810 //
3811 // Overload 0: returns _InternalReturnType
3812 //
3813 // This is the most basic task with no unwrapping
3814 //
3815 void _Init(details::_TypeSelectorNoAsync) const
3816 {
3817 this->_M_pTask->_FinalizeAndRunContinuations(
3818 _LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function)));
3819 }
3820
3821 //
3822 // Overload 1: returns IAsyncOperation<_InternalReturnType>^ (only under /ZW)
3823 // or
3824 // returns task<_InternalReturnType>
3825 //
3826 // This is task whose functor returns an async operation or a task which will be unwrapped for continuation
3827 // Depending on the output type, the right _AsyncInit gets invoked
3828 //
3829 void _Init(details::_TypeSelectorAsyncOperationOrTask) const
3830 {
3831 details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(
3832 this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function));
3833 }
3834
3835 #if defined(__cplusplus_winrt)
3836 //
3837 // Overload 2: returns IAsyncAction^
3838 //
3839 // This is task whose functor returns an async action which will be unwrapped for continuation
3840 //
3841 void _Init(details::_TypeSelectorAsyncAction) const
3842 {
3843 details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(
3844 this->_M_pTask,
3845 ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(_M_function)));
3846 }
3847
3848 //
3849 // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>^
3850 //
3851 // This is task whose functor returns an async operation with progress which will be unwrapped for continuation
3852 //
3853 void _Init(details::_TypeSelectorAsyncOperationWithProgress) const
3854 {
3855 typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType;
3856
3857 details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(
3858 this->_M_pTask,
3859 ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType,
3860 _ProgressType>(
3861 _LogWorkItemAndInvokeUserLambda(_M_function)));
3862 }
3863
3864 //
3865 // Overload 4: returns IAsyncActionWithProgress<_ProgressType>^
3866 //
3867 // This is task whose functor returns an async action with progress which will be unwrapped for continuation
3868 //
3869 void _Init(details::_TypeSelectorAsyncActionWithProgress) const
3870 {
3871 typedef details::_GetProgressType<decltype(_M_function())>::_Value _ProgressType;
3872
3873 details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(
3874 this->_M_pTask,
3875 ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(
3876 _LogWorkItemAndInvokeUserLambda(_M_function)));
3877 }
3878 #endif /* defined (__cplusplus_winrt) */
3879 };
3880
3881 /// <summary>
3882 /// The task handle type used to create a 'continuation task'.
3883 /// </summary>
3884 template<typename _InternalReturnType,
3885 typename _ContinuationReturnType,
3886 typename _Function,
3887 typename _IsTaskBased,
3888 typename _TypeSelection>
3889 struct _ContinuationTaskHandle
3890 : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type,
3891 _ContinuationTaskHandle<_InternalReturnType,
3892 _ContinuationReturnType,
3893 _Function,
3894 _IsTaskBased,
3895 _TypeSelection>,
3896 details::_ContinuationTaskHandleBase>
3897 {
3898 typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type
3899 _NormalizedContinuationReturnType;
3900
3901 typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl;
3902 typename details::_CopyableFunctor<typename std::decay<_Function>::type>::_Type _M_function;
3903
3904 template<class _ForwardedFunction>
3905 _ContinuationTaskHandle(
3906 const typename details::_Task_ptr<_ReturnType>::_Type& _AncestorImpl,
3907 const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type& _ContinuationImpl,
3908 _ForwardedFunction&& _Func,
3909 const task_continuation_context& _Context,
3910 details::_TaskInliningMode_t _InliningMode)
3911 : details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type,
3912 _ContinuationTaskHandle<_InternalReturnType,
3913 _ContinuationReturnType,
3914 _Function,
3915 _IsTaskBased,
3916 _TypeSelection>,
3917 details::_ContinuationTaskHandleBase>::_PPLTaskHandle(_ContinuationImpl)
3918 , _M_ancestorTaskImpl(_AncestorImpl)
3919 , _M_function(std::forward<_ForwardedFunction>(_Func))
3920 {
3921 this->_M_isTaskBasedContinuation = _IsTaskBased::value;
3922 this->_M_continuationContext = _Context;
3923 this->_M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware());
3924 this->_M_inliningMode = _InliningMode;
3925 }
3926
3927 virtual ~_ContinuationTaskHandle() {}
3928
3929 template<typename _Func, typename _Arg>
3930 auto _LogWorkItemAndInvokeUserLambda(_Func&& _func, _Arg&& _value) const
3931 -> decltype(_func(std::forward<_Arg>(_value)))
3932 {
3933 details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger);
3934 (void)_LogWorkItem;
3935 return _func(std::forward<_Arg>(_value));
3936 }
3937
3938 void _Perform() const { _Continue(_IsTaskBased(), _TypeSelection()); }
3939
3940 void _SyncCancelAndPropagateException() const
3941 {
3942 if (_M_ancestorTaskImpl->_HasUserException())
3943 {
3944 // If the ancestor encountered an exception, transfer the exception to the continuation
3945 // This traverses down the tree to propagate the exception.
3946 this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true);
3947 }
3948 else
3949 {
3950 // If the ancestor was canceled, then your own execution should be canceled.
3951 // This traverses down the tree to cancel it.
3952 this->_M_pTask->_Cancel(true);
3953 }
3954 }
3955
3956 //
3957 // Overload 0-0: _InternalReturnType -> _TaskType
3958 //
3959 // This is a straight task continuation which simply invokes its target with the ancestor's completion argument
3960 //
3961 void _Continue(std::false_type, details::_TypeSelectorNoAsync) const
3962 {
3963 this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(
3964 _Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function),
3965 _M_ancestorTaskImpl->_GetResult()));
3966 }
3967
3968 //
3969 // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>^ (only under /ZW)
3970 // or
3971 // _InternalReturnType -> task<_TaskType>
3972 //
3973 // This is a straight task continuation which returns an async operation or a task which will be unwrapped for
3974 // continuation Depending on the output type, the right _AsyncInit gets invoked
3975 //
3976 void _Continue(std::false_type, details::_TypeSelectorAsyncOperationOrTask) const
3977 {
3978 typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;
3979
3980 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
3981 this->_M_pTask,
3982 _LogWorkItemAndInvokeUserLambda(
3983 _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),
3984 _M_ancestorTaskImpl->_GetResult()));
3985 }
3986
3987 #if defined(__cplusplus_winrt)
3988 //
3989 // Overload 0-2: _InternalReturnType -> IAsyncAction^
3990 //
3991 // This is a straight task continuation which returns an async action which will be unwrapped for continuation
3992 //
3993 void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const
3994 {
3995 typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;
3996
3997 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
3998 this->_M_pTask,
3999 ref new details::_IAsyncActionToAsyncOperationConverter(_LogWorkItemAndInvokeUserLambda(
4000 _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),
4001 _M_ancestorTaskImpl->_GetResult())));
4002 }
4003
4004 //
4005 // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^
4006 //
4007 // This is a straight task continuation which returns an async operation with progress which will be unwrapped
4008 // for continuation
4009 //
4010 void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const
4011 {
4012 typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;
4013
4014 auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(
4015 _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),
4016 _M_ancestorTaskImpl->_GetResult());
4017 typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType;
4018
4019 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4020 this->_M_pTask,
4021 ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType,
4022 _ProgressType>(_OpWithProgress));
4023 }
4024
4025 //
4026 // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>^
4027 //
4028 // This is a straight task continuation which returns an async action with progress which will be unwrapped for
4029 // continuation
4030 //
4031 void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const
4032 {
4033 typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType;
4034
4035 auto _OpWithProgress = _LogWorkItemAndInvokeUserLambda(
4036 _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function),
4037 _M_ancestorTaskImpl->_GetResult());
4038 typedef details::_GetProgressType<decltype(_OpWithProgress)>::_Value _ProgressType;
4039
4040 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4041 this->_M_pTask,
4042 ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(_OpWithProgress));
4043 }
4044
4045 #endif /* defined (__cplusplus_winrt) */
4046
4047 //
4048 // Overload 1-0: task<_InternalReturnType> -> _TaskType
4049 //
4050 // This is an exception handling type of continuation which takes the task rather than the task's result.
4051 //
4052 void _Continue(std::true_type, details::_TypeSelectorNoAsync) const
4053 {
4054 typedef task<_InternalReturnType> _FuncInputType;
4055 task<_InternalReturnType> _ResultTask;
4056 _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));
4057 this->_M_pTask->_FinalizeAndRunContinuations(_LogWorkItemAndInvokeUserLambda(
4058 _Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function),
4059 std::move(_ResultTask)));
4060 }
4061
4062 //
4063 // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^
4064 // or
4065 // task<_TaskType>
4066 //
4067 // This is an exception handling type of continuation which takes the task rather than
4068 // the task's result. It also returns an async operation or a task which will be unwrapped
4069 // for continuation
4070 //
4071 void _Continue(std::true_type, details::_TypeSelectorAsyncOperationOrTask) const
4072 {
4073 // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.
4074 task<_InternalReturnType> _ResultTask;
4075 _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));
4076 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4077 this->_M_pTask, _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask)));
4078 }
4079
4080 #if defined(__cplusplus_winrt)
4081
4082 //
4083 // Overload 1-2: task<_InternalReturnType> -> IAsyncAction^
4084 //
4085 // This is an exception handling type of continuation which takes the task rather than
4086 // the task's result. It also returns an async action which will be unwrapped for continuation
4087 //
4088 void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const
4089 {
4090 // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.
4091 task<_InternalReturnType> _ResultTask;
4092 _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));
4093 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4094 this->_M_pTask,
4095 ref new details::_IAsyncActionToAsyncOperationConverter(
4096 _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));
4097 }
4098
4099 //
4100 // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>^
4101 //
4102 // This is an exception handling type of continuation which takes the task rather than
4103 // the task's result. It also returns an async operation with progress which will be unwrapped
4104 // for continuation
4105 //
4106 void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const
4107 {
4108 // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.
4109 task<_InternalReturnType> _ResultTask;
4110 _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));
4111
4112 typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType;
4113
4114 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4115 this->_M_pTask,
4116 ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType,
4117 _ProgressType>(
4118 _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));
4119 }
4120
4121 //
4122 // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>^
4123 //
4124 // This is an exception handling type of continuation which takes the task rather than
4125 // the task's result. It also returns an async operation with progress which will be unwrapped
4126 // for continuation
4127 //
4128 void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const
4129 {
4130 // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task.
4131 task<_InternalReturnType> _ResultTask;
4132 _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl));
4133
4134 typedef details::_GetProgressType<decltype(_M_function(_ResultTask))>::_Value _ProgressType;
4135
4136 details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
4137 this->_M_pTask,
4138 ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>(
4139 _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask))));
4140 }
4141 #endif /* defined (__cplusplus_winrt) */
4142 };
4143
4144 /// <summary>
4145 /// Initializes a task using a lambda, function pointer or function object.
4146 /// </summary>
4147 template<typename _InternalReturnType, typename _Function>
4148 void _TaskInitWithFunctor(const _Function& _Func)
4149 {
4150 typedef typename details::_InitFunctorTypeTraits<_InternalReturnType, decltype(_Func())> _Async_type_traits;
4151
4152 _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask;
4153 _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync;
4154 _M_Impl->_M_taskEventLogger._LogScheduleTask(false);
4155 _M_Impl->_ScheduleTask(
4156 new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(),
4157 _Func),
4158 details::_NoInline);
4159 }
4160
4161 /// <summary>
4162 /// Initializes a task using a task completion event.
4163 /// </summary>
4164 void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) { _Event._RegisterTask(_M_Impl); }
4165
4166 #if defined(__cplusplus_winrt)
4167 /// <summary>
4168 /// Initializes a task using an asynchronous operation IAsyncOperation<T>^
4169 /// </summary>
4170 void _TaskInitAsyncOp(
4171 Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)
4172 {
4173 _M_Impl->_M_fFromAsync = true;
4174
4175 // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once
4176 // _AsyncInit returns a completion could execute concurrently and the task must be fully initialized before that
4177 // happens.
4178 _M_Impl->_M_TaskState = details::_Task_impl_base::_Started;
4179 // Pass the shared pointer into _AsyncInit for storage in the Async Callback.
4180 details::_Task_impl_base::_AsyncInit<_ReturnType, _ReturnType>(_M_Impl, _AsyncOp);
4181 }
4182
4183 /// <summary>
4184 /// Initializes a task using an asynchronous operation IAsyncOperation<T>^
4185 /// </summary>
4186 void _TaskInitNoFunctor(
4187 Windows::Foundation::IAsyncOperation<typename details::_ValueTypeOrRefType<_ReturnType>::_Value> ^ _AsyncOp)
4188 {
4189 _TaskInitAsyncOp(_AsyncOp);
4190 }
4191
4192 /// <summary>
4193 /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress<T, P>^
4194 /// </summary>
4195 template<typename _Progress>
4196 void _TaskInitNoFunctor(
4197 Windows::Foundation::IAsyncOperationWithProgress<typename details::_ValueTypeOrRefType<_ReturnType>::_Value,
4198 _Progress> ^
4199 _AsyncOp)
4200 {
4201 _TaskInitAsyncOp(ref new details::_IAsyncOperationWithProgressToAsyncOperationConverter<
4202 typename details::_ValueTypeOrRefType<_ReturnType>::_Value,
4203 _Progress>(_AsyncOp));
4204 }
4205 #endif /* defined (__cplusplus_winrt) */
4206
4207 /// <summary>
4208 /// Initializes a task using a callable object.
4209 /// </summary>
4210 template<typename _Function>
4211 void _TaskInitMaybeFunctor(_Function& _Func, std::true_type)
4212 {
4213 _TaskInitWithFunctor<_ReturnType, _Function>(_Func);
4214 }
4215
4216 /// <summary>
4217 /// Initializes a task using a non-callable object.
4218 /// </summary>
4219 template<typename _Ty>
4220 void _TaskInitMaybeFunctor(_Ty& _Param, std::false_type)
4221 {
4222 _TaskInitNoFunctor(_Param);
4223 }
4224
4225 template<typename _InternalReturnType, typename _Function>
4226 auto _ThenImpl(_Function&& _Func, const task_options& _TaskOptions) const ->
4227 typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType
4228 {
4229 if (!_M_Impl)
4230 {
4231 throw invalid_operation("then() cannot be called on a default constructed task.");
4232 }
4233
4234 details::_CancellationTokenState* _PTokenState =
4235 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
4236 auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler();
4237 auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack
4238 ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack
4239 : details::_TaskCreationCallstack();
4240 return _ThenImpl<_InternalReturnType, _Function>(std::forward<_Function>(_Func),
4241 _PTokenState,
4242 _TaskOptions.get_continuation_context(),
4243 _Scheduler,
4244 _CreationStack);
4245 }
4246
4247 /// <summary>
4248 /// The one and only implementation of then for void and non-void tasks.
4249 /// </summary>
4250 template<typename _InternalReturnType, typename _Function>
4251 auto _ThenImpl(_Function&& _Func,
4252 details::_CancellationTokenState* _PTokenState,
4253 const task_continuation_context& _ContinuationContext,
4254 scheduler_ptr _Scheduler,
4255 details::_TaskCreationCallstack _CreationStack,
4256 details::_TaskInliningMode_t _InliningMode = details::_NoInline) const ->
4257 typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType
4258 {
4259 if (!_M_Impl)
4260 {
4261 throw invalid_operation("then() cannot be called on a default constructed task.");
4262 }
4263
4264 typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits;
4265 typedef details::_TaskTypeTraits<typename _Function_type_traits::_FuncRetType> _Async_type_traits;
4266 typedef typename _Async_type_traits::_TaskRetType _TaskType;
4267
4268 //
4269 // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the
4270 // antecedent's token UNLESS this is a an exception handling continuation. In that case, we break the chain with
4271 // a _None. That continuation is never canceled unless the user explicitly passes the same token.
4272 //
4273 if (_PTokenState == nullptr)
4274 {
4275 if (_Function_type_traits::_Takes_task::value)
4276 {
4277 _PTokenState = details::_CancellationTokenState::_None();
4278 }
4279 else
4280 {
4281 _PTokenState = _GetImpl()->_M_pTokenState;
4282 }
4283 }
4284
4285 task<_TaskType> _ContinuationTask;
4286 _ContinuationTask._CreateImpl(_PTokenState, _Scheduler);
4287
4288 _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask);
4289 _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync;
4290 _ContinuationTask._SetTaskCreationCallstack(_CreationStack);
4291
4292 _GetImpl()->_ScheduleContinuation(
4293 new _ContinuationTaskHandle<_InternalReturnType,
4294 _TaskType,
4295 _Function,
4296 typename _Function_type_traits::_Takes_task,
4297 typename _Async_type_traits::_AsyncKind>(_GetImpl(),
4298 _ContinuationTask._GetImpl(),
4299 std::forward<_Function>(_Func),
4300 _ContinuationContext,
4301 _InliningMode));
4302
4303 return _ContinuationTask;
4304 }
4305
4306 // The underlying implementation for this task
4307 typename details::_Task_ptr<_ReturnType>::_Type _M_Impl;
4308 };
4309
4310 /// <summary>
4311 /// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> object represents work that can be executed
4312 /// asynchronously, and concurrently with other tasks and parallel work produced by parallel algorithms in the
4313 /// Concurrency Runtime. It produces a result of type <typeparamref name="_ResultType"/> on successful completion.
4314 /// Tasks of type <c>task<void></c> produce no result. A task can be waited upon and canceled independently of
4315 /// other tasks. It can also be composed with other tasks using continuations(<c>then</c>), and
4316 /// join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.
4317 /// </summary>
4318 /// <remarks>
4319 /// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.
4320 /// </remarks>
4321 /**/
4322 template<>
4323 class task<void>
4324 {
4325 public:
4326 /// <summary>
4327 /// The type of the result an object of this class produces.
4328 /// </summary>
4329 /**/
4330 typedef void result_type;
4331
4332 /// <summary>
4333 /// Constructs a <c>task</c> object.
4334 /// </summary>
4335 /// <remarks>
4336 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
4337 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
4338 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
4339 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
4340 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
4341 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
4342 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
4343 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
4344 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
4345 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
4346 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
4347 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
4348 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
4349 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
4350 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
4351 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
4352 /// Runtime)"/>.</para>
4353 /// </remarks>
4354 /**/
4355 task() : _M_unitTask()
4356 {
4357 // The default constructor should create a task with a nullptr impl. This is a signal that the
4358 // task is not usable and should throw if any wait(), get() or then() APIs are used.
4359 }
4360
4361 /// <summary>
4362 /// Constructs a <c>task</c> object.
4363 /// </summary>
4364 /// <typeparam name="_Ty">
4365 /// The type of the parameter from which the task is to be constructed.
4366 /// </typeparam>
4367 /// <param name="_Param">
4368 /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a
4369 /// <c>task_completion_event<result_type></c> object, or a Windows::Foundation::IAsyncInfo if you are
4370 /// using tasks in your Windows Store app. The lambda or function object should be a type equivalent to
4371 /// <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
4372 /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
4373 /// </param>
4374 /// <remarks>
4375 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
4376 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
4377 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
4378 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
4379 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
4380 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
4381 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
4382 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
4383 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
4384 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
4385 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
4386 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
4387 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
4388 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
4389 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
4390 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
4391 /// Runtime)"/>.</para>
4392 /// </remarks>
4393 /**/
4394 template<typename _Ty>
4395 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
4396 explicit task(_Ty _Param, const task_options& _TaskOptions = task_options())
4397 {
4398 details::_ValidateTaskConstructorArgs<void, _Ty>(_Param);
4399
4400 _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler());
4401 // Do not move the next line out of this function. It is important that PPLX_CAPTURE_CALLSTACK() evaluate to the
4402 // the call site of the task constructor.
4403 _M_unitTask._SetTaskCreationCallstack(
4404 details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack
4405 ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack
4406 : PPLX_CAPTURE_CALLSTACK());
4407
4408 _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0));
4409 }
4410
4411 /// <summary>
4412 /// Constructs a <c>task</c> object.
4413 /// </summary>
4414 /// <param name="_Other">
4415 /// The source <c>task</c> object.
4416 /// </param>
4417 /// <remarks>
4418 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
4419 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
4420 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
4421 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
4422 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
4423 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
4424 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
4425 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
4426 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
4427 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
4428 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
4429 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
4430 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
4431 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
4432 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
4433 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
4434 /// Runtime)"/>.</para>
4435 /// </remarks>
4436 /**/
4437 task(const task& _Other) : _M_unitTask(_Other._M_unitTask) {}
4438
4439 /// <summary>
4440 /// Constructs a <c>task</c> object.
4441 /// </summary>
4442 /// <param name="_Other">
4443 /// The source <c>task</c> object.
4444 /// </param>
4445 /// <remarks>
4446 /// The default constructor for a <c>task</c> is only present in order to allow tasks to be used within
4447 /// containers. A default constructed task cannot be used until you assign a valid task to it. Methods such as
4448 /// <c>get</c>, <c>wait</c> or <c>then</c> will throw an <see cref="invalid_argument
4449 /// Class">invalid_argument</see> exception when called on a default constructed task. <para>A task that is
4450 /// created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the
4451 /// task completion event is set.</para> <para>The version of the constructor that takes a cancellation token
4452 /// creates a task that can be canceled using the <c>cancellation_token_source</c> the token was obtained from.
4453 /// Tasks created without a cancellation token are not cancelable.</para> <para>Tasks created from a
4454 /// <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
4455 /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes.
4456 /// Similarly, tasks created from a lambda that returns a <c>task<result_type></c> reach their terminal
4457 /// state when the inner task reaches its terminal state, and not when the lambda returns.</para>
4458 /// <para><c>task</c> behaves like a smart pointer and is safe to pass around by value. It can be accessed by
4459 /// multiple threads without the need for locks.</para> <para>The constructor overloads that take a
4460 /// Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available to
4461 /// Windows Store apps.</para> <para>For more information, see <see cref="Task Parallelism (Concurrency
4462 /// Runtime)"/>.</para>
4463 /// </remarks>
4464 /**/
4465 task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {}
4466
4467 /// <summary>
4468 /// Replaces the contents of one <c>task</c> object with another.
4469 /// </summary>
4470 /// <param name="_Other">
4471 /// The source <c>task</c> object.
4472 /// </param>
4473 /// <remarks>
4474 /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents
4475 /// the same actual task as <paramref name="_Other"/> does.
4476 /// </remarks>
4477 /**/
4478 task& operator=(const task& _Other)
4479 {
4480 if (this != &_Other)
4481 {
4482 _M_unitTask = _Other._M_unitTask;
4483 }
4484 return *this;
4485 }
4486
4487 /// <summary>
4488 /// Replaces the contents of one <c>task</c> object with another.
4489 /// </summary>
4490 /// <param name="_Other">
4491 /// The source <c>task</c> object.
4492 /// </param>
4493 /// <remarks>
4494 /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents
4495 /// the same actual task as <paramref name="_Other"/> does.
4496 /// </remarks>
4497 /**/
4498 task& operator=(task&& _Other)
4499 {
4500 if (this != &_Other)
4501 {
4502 _M_unitTask = std::move(_Other._M_unitTask);
4503 }
4504 return *this;
4505 }
4506
4507 /// <summary>
4508 /// Adds a continuation task to this task.
4509 /// </summary>
4510 /// <typeparam name="_Function">
4511 /// The type of the function object that will be invoked by this task.
4512 /// </typeparam>
4513 /// <param name="_Func">
4514 /// The continuation function to execute when this task completes. This continuation function must take as input
4515 /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the
4516 /// type of the result this task produces.
4517 /// </param>
4518 /// <param name="_CancellationToken">
4519 /// The cancellation token to associate with the continuation task. A continuation task that is created without
4520 /// a cancellation token will inherit the token of its antecedent task.
4521 /// </param>
4522 /// <returns>
4523 /// The newly created continuation task. The result type of the returned task is determined by what <paramref
4524 /// name="_Func"/> returns.
4525 /// </returns>
4526 /// <remarks>
4527 /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo
4528 /// interface, are only available to Windows Store apps. <para>For more information on how to use task
4529 /// continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
4530 /// </remarks>
4531 /**/
4532 template<typename _Function>
4533 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
4534 auto then(_Function&& _Func, task_options _TaskOptions = task_options()) const ->
4535 typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
4536 {
4537 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
4538 return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);
4539 }
4540
4541 /// <summary>
4542 /// Adds a continuation task to this task.
4543 /// </summary>
4544 /// <typeparam name="_Function">
4545 /// The type of the function object that will be invoked by this task.
4546 /// </typeparam>
4547 /// <param name="_Func">
4548 /// The continuation function to execute when this task completes. This continuation function must take as input
4549 /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the
4550 /// type of the result this task produces.
4551 /// </param>
4552 /// <param name="_CancellationToken">
4553 /// The cancellation token to associate with the continuation task. A continuation task that is created without
4554 /// a cancellation token will inherit the token of its antecedent task.
4555 /// </param>
4556 /// <param name="_ContinuationContext">
4557 /// A variable that specifies where the continuation should execute. This variable is only useful when used in a
4558 /// Windows Store style app. For more information, see <see cref="task_continuation_context
4559 /// Class">task_continuation_context</see>
4560 /// </param>
4561 /// <returns>
4562 /// The newly created continuation task. The result type of the returned task is determined by what <paramref
4563 /// name="_Func"/> returns.
4564 /// </returns>
4565 /// <remarks>
4566 /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo
4567 /// interface, are only available to Windows Store apps. <para>For more information on how to use task
4568 /// continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
4569 /// </remarks>
4570 /**/
4571 template<typename _Function>
4572 __declspec(noinline) // Ask for no inlining so that the PPLX_CAPTURE_CALLSTACK gives us the expected result
4573 auto then(_Function&& _Func,
4574 cancellation_token _CancellationToken,
4575 task_continuation_context _ContinuationContext) const ->
4576 typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
4577 {
4578 task_options _TaskOptions(_CancellationToken, _ContinuationContext);
4579 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
4580 return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func), _TaskOptions);
4581 }
4582
4583 /// <summary>
4584 /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> to execute the task inline, if
4585 /// all of the tasks dependencies are satisfied, and it has not already been picked up for execution by a
4586 /// background worker.
4587 /// </summary>
4588 /// <returns>
4589 /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task
4590 /// encountered an exception during execution, or an exception was propagated to it from an antecedent task,
4591 /// <c>wait</c> will throw that exception.
4592 /// </returns>
4593 /**/
4594 task_status wait() const { return _M_unitTask.wait(); }
4595
4596 /// <summary>
4597 /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will
4598 /// wait for the task to finish. This method does not return a value when called on a task with a
4599 /// <c>result_type</c> of <c>void</c>.
4600 /// </summary>
4601 /// <remarks>
4602 /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled
4603 /// Class">task_canceled</see> exception. If the task encountered an different exception or an exception was
4604 /// propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.
4605 /// </remarks>
4606 /**/
4607 void get() const { _M_unitTask.get(); }
4608
4609 /// <summary>
4610 /// Determines if the task is completed.
4611 /// </summary>
4612 /// <returns>
4613 /// True if the task has completed, false otherwise.
4614 /// </returns>
4615 /// <remarks>
4616 /// The function returns true if the task is completed or canceled (with or without user exception).
4617 /// </remarks>
4618 bool is_done() const { return _M_unitTask.is_done(); }
4619
4620 /// <summary>
4621 /// Returns the scheduler for this task
4622 /// </summary>
4623 /// <returns>
4624 /// A pointer to the scheduler
4625 /// </returns>
4626 scheduler_ptr scheduler() const { return _M_unitTask.scheduler(); }
4627
4628 /// <summary>
4629 /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such
4630 /// a task.
4631 /// </summary>
4632 /// <returns>
4633 /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task,
4634 /// <c>false</c> otherwise.
4635 /// </returns>
4636 /**/
4637 bool is_apartment_aware() const { return _M_unitTask.is_apartment_aware(); }
4638
4639 /// <summary>
4640 /// Determines whether two <c>task</c> objects represent the same internal task.
4641 /// </summary>
4642 /// <returns>
4643 /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.
4644 /// </returns>
4645 /**/
4646 bool operator==(const task<void>& _Rhs) const { return (_M_unitTask == _Rhs._M_unitTask); }
4647
4648 /// <summary>
4649 /// Determines whether two <c>task</c> objects represent different internal tasks.
4650 /// </summary>
4651 /// <returns>
4652 /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.
4653 /// </returns>
4654 /**/
4655 bool operator!=(const task<void>& _Rhs) const { return !operator==(_Rhs); }
4656
4657 /// <summary>
4658 /// Create an underlying task implementation.
4659 /// </summary>
4660 void _CreateImpl(details::_CancellationTokenState* _Ct, scheduler_ptr _Scheduler)
4661 {
4662 _M_unitTask._CreateImpl(_Ct, _Scheduler);
4663 }
4664
4665 /// <summary>
4666 /// Return the underlying implementation for this task.
4667 /// </summary>
4668 const details::_Task_ptr<details::_Unit_type>::_Type& _GetImpl() const { return _M_unitTask._M_Impl; }
4669
4670 /// <summary>
4671 /// Set the implementation of the task to be the supplied implementation.
4672 /// </summary>
4673 void _SetImpl(const details::_Task_ptr<details::_Unit_type>::_Type& _Impl) { _M_unitTask._SetImpl(_Impl); }
4674
4675 /// <summary>
4676 /// Set the implementation of the task to be the supplied implementation using a move instead of a copy.
4677 /// </summary>
4678 void _SetImpl(details::_Task_ptr<details::_Unit_type>::_Type&& _Impl) { _M_unitTask._SetImpl(std::move(_Impl)); }
4679
4680 /// <summary>
4681 /// Sets a property determining whether the task is apartment aware.
4682 /// </summary>
4683 void _SetAsync(bool _Async = true) { _M_unitTask._SetAsync(_Async); }
4684
4685 /// <summary>
4686 /// Sets a field in the task impl to the return callstack for calls to the task constructors and the then
4687 /// method.
4688 /// </summary>
4689 void _SetTaskCreationCallstack(const details::_TaskCreationCallstack& _callstack)
4690 {
4691 _M_unitTask._SetTaskCreationCallstack(_callstack);
4692 }
4693
4694 /// <summary>
4695 /// An internal version of then that takes additional flags and executes the continuation inline. Used for
4696 /// runtime internal continuations only.
4697 /// </summary>
4698 template<typename _Function>
4699 auto _Then(_Function&& _Func,
4700 details::_CancellationTokenState* _PTokenState,
4701 details::_TaskInliningMode_t _InliningMode = details::_ForceInline) const ->
4702 typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
4703 {
4704 // inherit from antecedent
4705 auto _Scheduler = _GetImpl()->_GetScheduler();
4706
4707 return _M_unitTask._ThenImpl<void, _Function>(std::forward<_Function>(_Func),
4708 _PTokenState,
4709 task_continuation_context::use_default(),
4710 _Scheduler,
4711 PPLX_CAPTURE_CALLSTACK(),
4712 _InliningMode);
4713 }
4714
4715 private:
4716 template<typename T>
4717 friend class task;
4718 template<typename T>
4719 friend class task_completion_event;
4720
4721 /// <summary>
4722 /// Initializes a task using a task completion event.
4723 /// </summary>
4724 void _TaskInitNoFunctor(task_completion_event<void>& _Event)
4725 {
4726 _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent);
4727 }
4728
4729 #if defined(__cplusplus_winrt)
4730 /// <summary>
4731 /// Initializes a task using an asynchronous action IAsyncAction^
4732 /// </summary>
4733 void _TaskInitNoFunctor(Windows::Foundation::IAsyncAction ^ _AsyncAction)
4734 {
4735 _M_unitTask._TaskInitAsyncOp(ref new details::_IAsyncActionToAsyncOperationConverter(_AsyncAction));
4736 }
4737
4738 /// <summary>
4739 /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>^
4740 /// </summary>
4741 template<typename _P>
4742 void _TaskInitNoFunctor(Windows::Foundation::IAsyncActionWithProgress<_P> ^ _AsyncActionWithProgress)
4743 {
4744 _M_unitTask._TaskInitAsyncOp(
4745 ref new details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>(_AsyncActionWithProgress));
4746 }
4747 #endif /* defined (__cplusplus_winrt) */
4748
4749 /// <summary>
4750 /// Initializes a task using a callable object.
4751 /// </summary>
4752 template<typename _Function>
4753 void _TaskInitMaybeFunctor(_Function& _Func, std::true_type)
4754 {
4755 _M_unitTask._TaskInitWithFunctor<void, _Function>(_Func);
4756 }
4757
4758 /// <summary>
4759 /// Initializes a task using a non-callable object.
4760 /// </summary>
4761 template<typename _T>
4762 void _TaskInitMaybeFunctor(_T& _Param, std::false_type)
4763 {
4764 _TaskInitNoFunctor(_Param);
4765 }
4766
4767 // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void
4768 // results.
4769 task<details::_Unit_type> _M_unitTask;
4770 };
4771
4772 namespace details
4773 {
4774 /// <summary>
4775 /// The following type traits are used for the create_task function.
4776 /// </summary>
4777
4778 #if defined(__cplusplus_winrt)
4779 // Unwrap functions for asyncOperations
4780 template<typename _Ty>
4781 _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperation<_Ty> ^);
4782
4783 void _GetUnwrappedType(Windows::Foundation::IAsyncAction ^);
4784
4785 template<typename _Ty, typename _Progress>
4786 _Ty _GetUnwrappedType(Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress> ^);
4787
4788 template<typename _Progress>
4789 void _GetUnwrappedType(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^);
4790 #endif /* defined (__cplusplus_winrt) */
4791
4792 // Unwrap task<T>
4793 template<typename _Ty>
4794 _Ty _GetUnwrappedType(task<_Ty>);
4795
4796 // Unwrap all supported types
4797 template<typename _Ty>
4798 auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg));
4799 // fallback
4800 template<typename _Ty>
4801 _Ty _GetUnwrappedReturnType(_Ty, ...);
4802
4803 /// <summary>
4804 /// <c>_GetTaskType</c> functions will retrieve task type <c>T</c> in <c>task[T](Arg)</c>,
4805 /// for given constructor argument <c>Arg</c> and its property "callable".
4806 /// It will automatically unwrap argument to get the final return type if necessary.
4807 /// </summary>
4808
4809 // Non-Callable
4810 template<typename _Ty>
4811 _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type);
4812
4813 // Non-Callable
4814 template<typename _Ty>
4815 auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc));
4816
4817 // Callable
4818 template<typename _Ty>
4819 auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(_Func(), 0));
4820
4821 // Special callable returns void
4822 void _GetTaskType(std::function<void()>, std::true_type);
4823 struct _BadArgType
4824 {
4825 };
4826
4827 template<typename _Ty>
4828 auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable(_Param, 0)));
4829
4830 template<typename _Ty>
4831 _BadArgType _FilterValidTaskType(_Ty _Param, ...);
4832
4833 template<typename _Ty>
4834 struct _TaskTypeFromParam
4835 {
4836 typedef decltype(_FilterValidTaskType(stdx::declval<_Ty>(), 0)) _Type;
4837 };
4838 } // namespace details
4839
4840 /// <summary>
4841 /// Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have
4842 /// used a task constructor. It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword
4843 /// while creating tasks.
4844 /// </summary>
4845 /// <typeparam name="_Ty">
4846 /// The type of the parameter from which the task is to be constructed.
4847 /// </typeparam>
4848 /// <param name="_Param">
4849 /// The parameter from which the task is to be constructed. This could be a lambda or function object, a
4850 /// <c>task_completion_event</c> object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo
4851 /// interface if you are using tasks in your Windows Store app.
4852 /// </param>
4853 /// <returns>
4854 /// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.
4855 /// </returns>
4856 /// <remarks>
4857 /// The first overload behaves like a task constructor that takes a single parameter.
4858 /// <para>The second overload associates the cancellation token provided with the newly created task. If you use
4859 /// this overload you are not allowed to pass in a different <c>task</c> object as the first parameter.</para>
4860 /// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref
4861 /// name="_Param"/> is a <c>task_completion_event<T></c>, a <c>task<T></c>, or a functor that returns
4862 /// either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.</para>
4863 /// <para>In a Windows Store app, if <paramref name="_Param"/> is of type
4864 /// Windows::Foundation::IAsyncOperation<T>^ or Windows::Foundation::IAsyncOperationWithProgress<T,P>^,
4865 /// or a functor that returns either of those types, the created task will be of type <c>task<T></c>. If
4866 /// <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or
4867 /// Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor that returns either of those types, the
4868 /// created task will have type <c>task<void></c>.</para>
4869 /// </remarks>
4870 /// <seealso cref="task Class"/>
4871 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
4872 /**/
4873 template<typename _Ty>
4874 __declspec(noinline) auto create_task(_Ty _Param, task_options _TaskOptions = task_options())
4875 -> task<typename details::_TaskTypeFromParam<_Ty>::_Type>
4876 {
4877 static_assert(!std::is_same<typename details::_TaskTypeFromParam<_Ty>::_Type, details::_BadArgType>::value,
4878 #if defined(__cplusplus_winrt)
4879 "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a "
4880 "task_completion_event"
4881 #else /* defined (__cplusplus_winrt) */
4882 "incorrect argument for create_task; can be a callable object or a task_completion_event"
4883 #endif /* defined (__cplusplus_winrt) */
4884 );
4885 details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(PPLX_CAPTURE_CALLSTACK());
4886 task<typename details::_TaskTypeFromParam<_Ty>::_Type> _CreatedTask(_Param, _TaskOptions);
4887 return _CreatedTask;
4888 }
4889
4890 /// <summary>
4891 /// Creates a PPL <see cref="task Class">task</see> object. <c>create_task</c> can be used anywhere you would have
4892 /// used a task constructor. It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword
4893 /// while creating tasks.
4894 /// </summary>
4895 /// <typeparam name="_Ty">
4896 /// The type of the parameter from which the task is to be constructed.
4897 /// </typeparam>
4898 /// <param name="_Param">
4899 /// The parameter from which the task is to be constructed. This could be a lambda or function object, a
4900 /// <c>task_completion_event</c> object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo
4901 /// interface if you are using tasks in your Windows Store app.
4902 /// </param>
4903 /// <param name="_Token">
4904 /// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will
4905 /// be requested on the task.
4906 /// </param>
4907 /// <returns>
4908 /// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.
4909 /// </returns>
4910 /// <remarks>
4911 /// The first overload behaves like a task constructor that takes a single parameter.
4912 /// <para>The second overload associates the cancellation token provided with the newly created task. If you use
4913 /// this overload you are not allowed to pass in a different <c>task</c> object as the first parameter.</para>
4914 /// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref
4915 /// name="_Param"/> is a <c>task_completion_event<T></c>, a <c>task<T></c>, or a functor that returns
4916 /// either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.</para>
4917 /// <para>In a Windows Store app, if <paramref name="_Param"/> is of type
4918 /// Windows::Foundation::IAsyncOperation<T>^ or Windows::Foundation::IAsyncOperationWithProgress<T,P>^,
4919 /// or a functor that returns either of those types, the created task will be of type <c>task<T></c>. If
4920 /// <paramref name="_Param"/> is of type Windows::Foundation::IAsyncAction^ or
4921 /// Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor that returns either of those types, the
4922 /// created task will have type <c>task<void></c>.</para>
4923 /// </remarks>
4924 /// <seealso cref="task Class"/>
4925 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
4926 /**/
4927 template<typename _ReturnType>
4928 __declspec(noinline) task<_ReturnType> create_task(const task<_ReturnType>& _Task)
4929 {
4930 task<_ReturnType> _CreatedTask(_Task);
4931 return _CreatedTask;
4932 }
4933
4934 #if defined(__cplusplus_winrt)
4935 namespace details
4936 {
4937 template<typename _T>
4938 task<_T> _To_task_helper(Windows::Foundation::IAsyncOperation<_T> ^ op)
4939 {
4940 return task<_T>(op);
4941 }
4942
4943 template<typename _T, typename _Progress>
4944 task<_T> _To_task_helper(Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress> ^ op)
4945 {
4946 return task<_T>(op);
4947 }
4948
4949 inline task<void> _To_task_helper(Windows::Foundation::IAsyncAction ^ op) { return task<void>(op); }
4950
4951 template<typename _Progress>
4952 task<void> _To_task_helper(Windows::Foundation::IAsyncActionWithProgress<_Progress> ^ op)
4953 {
4954 return task<void>(op);
4955 }
4956
4957 template<typename _ProgressType>
4958 class _ProgressDispatcherBase
4959 {
4960 public:
4961 virtual ~_ProgressDispatcherBase() {}
4962
4963 virtual void _Report(const _ProgressType& _Val) = 0;
4964 };
4965
4966 template<typename _ProgressType, typename _ClassPtrType>
4967 class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType>
4968 {
4969 public:
4970 virtual ~_ProgressDispatcher() {}
4971
4972 _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) {}
4973
4974 virtual void _Report(const _ProgressType& _Val) { _M_ptr->_FireProgress(_Val); }
4975
4976 private:
4977 _ClassPtrType _M_ptr;
4978 };
4979 class _ProgressReporterCtorArgType
4980 {
4981 };
4982 } // namespace details
4983
4984 /// <summary>
4985 /// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter
4986 /// object is bound to a particular asynchronous action or operation.
4987 /// </summary>
4988 /// <typeparam name="_ProgressType">
4989 /// The payload type of each progress notification reported through the progress reporter.
4990 /// </typeparam>
4991 /// <remarks>
4992 /// This type is only available to Windows Store apps.
4993 /// </remarks>
4994 /// <seealso cref="create_async Function"/>
4995 /**/
4996 template<typename _ProgressType>
4997 class progress_reporter
4998 {
4999 typedef std::shared_ptr<details::_ProgressDispatcherBase<_ProgressType>> _PtrType;
5000
5001 public:
5002 /// <summary>
5003 /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound.
5004 /// </summary>
5005 /// <param name="_Val">
5006 /// The payload to report through a progress notification.
5007 /// </param>
5008 /**/
5009 void report(const _ProgressType& _Val) const { _M_dispatcher->_Report(_Val); }
5010
5011 template<typename _ClassPtrType>
5012 static progress_reporter _CreateReporter(_ClassPtrType _Ptr)
5013 {
5014 progress_reporter _Reporter;
5015 details::_ProgressDispatcherBase<_ProgressType>* _PDispatcher =
5016 new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr);
5017 _Reporter._M_dispatcher = _PtrType(_PDispatcher);
5018 return _Reporter;
5019 }
5020 progress_reporter() {}
5021
5022 private:
5023 progress_reporter(details::_ProgressReporterCtorArgType);
5024
5025 _PtrType _M_dispatcher;
5026 };
5027
5028 namespace details
5029 {
5030 //
5031 // maps internal definitions for AsyncStatus and defines states that are not client visible
5032 //
5033 enum _AsyncStatusInternal
5034 {
5035 _AsyncCreated = -1, // externally invisible
5036 // client visible states (must match AsyncStatus exactly)
5037 _AsyncStarted = 0, // Windows::Foundation::AsyncStatus::Started,
5038 _AsyncCompleted = 1, // Windows::Foundation::AsyncStatus::Completed,
5039 _AsyncCanceled = 2, // Windows::Foundation::AsyncStatus::Canceled,
5040 _AsyncError = 3, // Windows::Foundation::AsyncStatus::Error,
5041 // non-client visible internal states
5042 _AsyncCancelPending,
5043 _AsyncClosed,
5044 _AsyncUndefined
5045 };
5046
5047 //
5048 // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results
5049 // (which are progressively consumable between Start state and before Close is called)
5050 //
5051 enum _AsyncResultType
5052 {
5053 SingleResult = 0x0001,
5054 MultipleResults = 0x0002
5055 };
5056
5057 // ***************************************************************************
5058 // Template type traits and helpers for async production APIs:
5059 //
5060
5061 struct _ZeroArgumentFunctor
5062 {
5063 };
5064 struct _OneArgumentFunctor
5065 {
5066 };
5067 struct _TwoArgumentFunctor
5068 {
5069 };
5070
5071 // ****************************************
5072 // CLASS TYPES:
5073
5074 // ********************
5075 // TWO ARGUMENTS:
5076
5077 // non-void arg:
5078 template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
5079 _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);
5080
5081 // non-void arg:
5082 template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
5083 _Arg2 _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);
5084
5085 template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
5086 _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1, _Arg2) const);
5087
5088 template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
5089 _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1, _Arg2) const);
5090
5091 // ********************
5092 // ONE ARGUMENT:
5093
5094 // non-void arg:
5095 template<typename _Class, typename _ReturnType, typename _Arg1>
5096 _Arg1 _Arg1ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);
5097
5098 // non-void arg:
5099 template<typename _Class, typename _ReturnType, typename _Arg1>
5100 void _Arg2ClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);
5101
5102 template<typename _Class, typename _ReturnType, typename _Arg1>
5103 _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)(_Arg1) const);
5104
5105 template<typename _Class, typename _ReturnType, typename _Arg1>
5106 _OneArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)(_Arg1) const);
5107
5108 // ********************
5109 // ZERO ARGUMENT:
5110
5111 // void arg:
5112 template<typename _Class, typename _ReturnType>
5113 void _Arg1ClassHelperThunk(_ReturnType (_Class::*)() const);
5114
5115 // void arg:
5116 template<typename _Class, typename _ReturnType>
5117 void _Arg2ClassHelperThunk(_ReturnType (_Class::*)() const);
5118
5119 // void arg:
5120 template<typename _Class, typename _ReturnType>
5121 _ReturnType _ReturnTypeClassHelperThunk(_ReturnType (_Class::*)() const);
5122
5123 template<typename _Class, typename _ReturnType>
5124 _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType (_Class::*)() const);
5125
5126 // ****************************************
5127 // POINTER TYPES:
5128
5129 // ********************
5130 // TWO ARGUMENTS:
5131
5132 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5133 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));
5134
5135 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5136 _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));
5137
5138 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5139 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)(_Arg1, _Arg2));
5140
5141 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5142 _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)(_Arg1, _Arg2));
5143
5144 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5145 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));
5146
5147 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5148 _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));
5149
5150 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5151 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)(_Arg1, _Arg2));
5152
5153 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5154 _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)(_Arg1, _Arg2));
5155
5156 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5157 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));
5158
5159 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5160 _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));
5161
5162 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5163 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)(_Arg1, _Arg2));
5164
5165 template<typename _ReturnType, typename _Arg1, typename _Arg2>
5166 _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)(_Arg1, _Arg2));
5167
5168 // ********************
5169 // ONE ARGUMENT:
5170
5171 template<typename _ReturnType, typename _Arg1>
5172 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));
5173
5174 template<typename _ReturnType, typename _Arg1>
5175 void _Arg2PFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));
5176
5177 template<typename _ReturnType, typename _Arg1>
5178 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)(_Arg1));
5179
5180 template<typename _ReturnType, typename _Arg1>
5181 _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)(_Arg1));
5182
5183 template<typename _ReturnType, typename _Arg1>
5184 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));
5185
5186 template<typename _ReturnType, typename _Arg1>
5187 void _Arg2PFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));
5188
5189 template<typename _ReturnType, typename _Arg1>
5190 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)(_Arg1));
5191
5192 template<typename _ReturnType, typename _Arg1>
5193 _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)(_Arg1));
5194
5195 template<typename _ReturnType, typename _Arg1>
5196 _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));
5197
5198 template<typename _ReturnType, typename _Arg1>
5199 void _Arg2PFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));
5200
5201 template<typename _ReturnType, typename _Arg1>
5202 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)(_Arg1));
5203
5204 template<typename _ReturnType, typename _Arg1>
5205 _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)(_Arg1));
5206
5207 // ********************
5208 // ZERO ARGUMENT:
5209
5210 template<typename _ReturnType>
5211 void _Arg1PFNHelperThunk(_ReturnType(__cdecl*)());
5212
5213 template<typename _ReturnType>
5214 void _Arg2PFNHelperThunk(_ReturnType(__cdecl*)());
5215
5216 template<typename _ReturnType>
5217 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl*)());
5218
5219 template<typename _ReturnType>
5220 _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl*)());
5221
5222 template<typename _ReturnType>
5223 void _Arg1PFNHelperThunk(_ReturnType(__stdcall*)());
5224
5225 template<typename _ReturnType>
5226 void _Arg2PFNHelperThunk(_ReturnType(__stdcall*)());
5227
5228 template<typename _ReturnType>
5229 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall*)());
5230
5231 template<typename _ReturnType>
5232 _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall*)());
5233
5234 template<typename _ReturnType>
5235 void _Arg1PFNHelperThunk(_ReturnType(__fastcall*)());
5236
5237 template<typename _ReturnType>
5238 void _Arg2PFNHelperThunk(_ReturnType(__fastcall*)());
5239
5240 template<typename _ReturnType>
5241 _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall*)());
5242
5243 template<typename _ReturnType>
5244 _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall*)());
5245
5246 template<typename _T>
5247 struct _FunctorArguments
5248 {
5249 static const size_t _Count = 0;
5250 };
5251
5252 template<>
5253 struct _FunctorArguments<_OneArgumentFunctor>
5254 {
5255 static const size_t _Count = 1;
5256 };
5257
5258 template<>
5259 struct _FunctorArguments<_TwoArgumentFunctor>
5260 {
5261 static const size_t _Count = 2;
5262 };
5263
5264 template<typename _T>
5265 struct _FunctorTypeTraits
5266 {
5267 typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType;
5268 static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count;
5269
5270 typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType;
5271 typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type;
5272 typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type;
5273 };
5274
5275 template<typename _T>
5276 struct _FunctorTypeTraits<_T*>
5277 {
5278 typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType;
5279 static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count;
5280
5281 typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType;
5282 typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type;
5283 typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type;
5284 };
5285
5286 template<typename _T>
5287 struct _ProgressTypeTraits
5288 {
5289 static const bool _TakesProgress = false;
5290 typedef void _ProgressType;
5291 };
5292
5293 template<typename _T>
5294 struct _ProgressTypeTraits<progress_reporter<_T>>
5295 {
5296 static const bool _TakesProgress = true;
5297 typedef typename _T _ProgressType;
5298 };
5299
5300 template<typename _T, size_t count = _FunctorTypeTraits<_T>::_ArgumentCount>
5301 struct _CAFunctorOptions
5302 {
5303 static const bool _TakesProgress = false;
5304 static const bool _TakesToken = false;
5305 typedef void _ProgressType;
5306 };
5307
5308 template<typename _T>
5309 struct _CAFunctorOptions<_T, 1>
5310 {
5311 private:
5312 typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type;
5313
5314 public:
5315 static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress;
5316 static const bool _TakesToken = !_TakesProgress;
5317 typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType;
5318 };
5319
5320 template<typename _T>
5321 struct _CAFunctorOptions<_T, 2>
5322 {
5323 private:
5324 typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type;
5325
5326 public:
5327 static const bool _TakesProgress = true;
5328 static const bool _TakesToken = true;
5329 typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType;
5330 };
5331
5332 ref class _Zip
5333 {
5334 };
5335
5336 // ***************************************************************************
5337 // Async Operation Task Generators
5338 //
5339
5340 //
5341 // Functor returns an IAsyncInfo - result needs to be wrapped in a task:
5342 //
5343 template<typename _AsyncSelector, typename _ReturnType>
5344 struct _SelectorTaskGenerator
5345 {
5346 template<typename _Function>
5347 static task<_ReturnType> _GenerateTask_0(const _Function& _Func,
5348 cancellation_token_source _Cts,
5349 const _TaskCreationCallstack& _callstack)
5350 {
5351 task_options _taskOptinos(_Cts.get_token());
5352 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5353 return task<_ReturnType>(_Func(), _taskOptinos);
5354 }
5355
5356 template<typename _Function>
5357 static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,
5358 cancellation_token_source _Cts,
5359 const _TaskCreationCallstack& _callstack)
5360 {
5361 task_options _taskOptinos(_Cts.get_token());
5362 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5363 return task<_ReturnType>(_Func(_Cts.get_token()), _taskOptinos);
5364 }
5365
5366 template<typename _Function, typename _ProgressObject>
5367 static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,
5368 const _ProgressObject& _Progress,
5369 cancellation_token_source _Cts,
5370 const _TaskCreationCallstack& _callstack)
5371 {
5372 task_options _taskOptinos(_Cts.get_token());
5373 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5374 return task<_ReturnType>(_Func(_Progress), _taskOptinos);
5375 }
5376
5377 template<typename _Function, typename _ProgressObject>
5378 static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,
5379 const _ProgressObject& _Progress,
5380 cancellation_token_source _Cts,
5381 const _TaskCreationCallstack& _callstack)
5382 {
5383 task_options _taskOptinos(_Cts.get_token());
5384 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5385 return task<_ReturnType>(_Func(_Progress, _Cts.get_token()), _taskOptinos);
5386 }
5387 };
5388
5389 template<typename _AsyncSelector>
5390 struct _SelectorTaskGenerator<_AsyncSelector, void>
5391 {
5392 template<typename _Function>
5393 static task<void> _GenerateTask_0(const _Function& _Func,
5394 cancellation_token_source _Cts,
5395 const _TaskCreationCallstack& _callstack)
5396 {
5397 task_options _taskOptinos(_Cts.get_token());
5398 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5399 return task<void>(_Func(), _taskOptinos);
5400 }
5401
5402 template<typename _Function>
5403 static task<void> _GenerateTask_1C(const _Function& _Func,
5404 cancellation_token_source _Cts,
5405 const _TaskCreationCallstack& _callstack)
5406 {
5407 task_options _taskOptinos(_Cts.get_token());
5408 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5409 return task<void>(_Func(_Cts.get_token()), _taskOptinos);
5410 }
5411
5412 template<typename _Function, typename _ProgressObject>
5413 static task<void> _GenerateTask_1P(const _Function& _Func,
5414 const _ProgressObject& _Progress,
5415 cancellation_token_source _Cts,
5416 const _TaskCreationCallstack& _callstack)
5417 {
5418 task_options _taskOptinos(_Cts.get_token());
5419 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5420 return task<void>(_Func(_Progress), _taskOptinos);
5421 }
5422
5423 template<typename _Function, typename _ProgressObject>
5424 static task<void> _GenerateTask_2PC(const _Function& _Func,
5425 const _ProgressObject& _Progress,
5426 cancellation_token_source _Cts,
5427 const _TaskCreationCallstack& _callstack)
5428 {
5429 task_options _taskOptinos(_Cts.get_token());
5430 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5431 return task<void>(_Func(_Progress, _Cts.get_token()), _taskOptinos);
5432 }
5433 };
5434
5435 //
5436 // Functor returns a result - it needs to be wrapped in a task:
5437 //
5438 template<typename _ReturnType>
5439 struct _SelectorTaskGenerator<_TypeSelectorNoAsync, _ReturnType>
5440 {
5441
5442 #pragma warning(push)
5443 #pragma warning(disable : 4702)
5444 template<typename _Function>
5445 static task<_ReturnType> _GenerateTask_0(const _Function& _Func,
5446 cancellation_token_source _Cts,
5447 const _TaskCreationCallstack& _callstack)
5448 {
5449 task_options _taskOptinos(_Cts.get_token());
5450 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5451 return task<_ReturnType>(
5452 [=]() -> _ReturnType {
5453 _Task_generator_oversubscriber_t _Oversubscriber;
5454 (_Oversubscriber);
5455 return _Func();
5456 },
5457 _taskOptinos);
5458 }
5459 #pragma warning(pop)
5460
5461 template<typename _Function>
5462 static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,
5463 cancellation_token_source _Cts,
5464 const _TaskCreationCallstack& _callstack)
5465 {
5466 task_options _taskOptinos(_Cts.get_token());
5467 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5468 return task<_ReturnType>(
5469 [=]() -> _ReturnType {
5470 _Task_generator_oversubscriber_t _Oversubscriber;
5471 (_Oversubscriber);
5472 return _Func(_Cts.get_token());
5473 },
5474 _taskOptinos);
5475 }
5476
5477 template<typename _Function, typename _ProgressObject>
5478 static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,
5479 const _ProgressObject& _Progress,
5480 cancellation_token_source _Cts,
5481 const _TaskCreationCallstack& _callstack)
5482 {
5483 task_options _taskOptinos(_Cts.get_token());
5484 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5485 return task<_ReturnType>(
5486 [=]() -> _ReturnType {
5487 _Task_generator_oversubscriber_t _Oversubscriber;
5488 (_Oversubscriber);
5489 return _Func(_Progress);
5490 },
5491 _taskOptinos);
5492 }
5493
5494 template<typename _Function, typename _ProgressObject>
5495 static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,
5496 const _ProgressObject& _Progress,
5497 cancellation_token_source _Cts,
5498 const _TaskCreationCallstack& _callstack)
5499 {
5500 task_options _taskOptinos(_Cts.get_token());
5501 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5502 return task<_ReturnType>(
5503 [=]() -> _ReturnType {
5504 _Task_generator_oversubscriber_t _Oversubscriber;
5505 (_Oversubscriber);
5506 return _Func(_Progress, _Cts.get_token());
5507 },
5508 _taskOptinos);
5509 }
5510 };
5511
5512 template<>
5513 struct _SelectorTaskGenerator<_TypeSelectorNoAsync, void>
5514 {
5515 template<typename _Function>
5516 static task<void> _GenerateTask_0(const _Function& _Func,
5517 cancellation_token_source _Cts,
5518 const _TaskCreationCallstack& _callstack)
5519 {
5520 task_options _taskOptinos(_Cts.get_token());
5521 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5522 return task<void>(
5523 [=]() {
5524 _Task_generator_oversubscriber_t _Oversubscriber;
5525 (_Oversubscriber);
5526 _Func();
5527 },
5528 _taskOptinos);
5529 }
5530
5531 template<typename _Function>
5532 static task<void> _GenerateTask_1C(const _Function& _Func,
5533 cancellation_token_source _Cts,
5534 const _TaskCreationCallstack& _callstack)
5535 {
5536 task_options _taskOptinos(_Cts.get_token());
5537 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5538 return task<void>(
5539 [=]() {
5540 _Task_generator_oversubscriber_t _Oversubscriber;
5541 (_Oversubscriber);
5542 _Func(_Cts.get_token());
5543 },
5544 _taskOptinos);
5545 }
5546
5547 template<typename _Function, typename _ProgressObject>
5548 static task<void> _GenerateTask_1P(const _Function& _Func,
5549 const _ProgressObject& _Progress,
5550 cancellation_token_source _Cts,
5551 const _TaskCreationCallstack& _callstack)
5552 {
5553 task_options _taskOptinos(_Cts.get_token());
5554 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5555 return task<void>(
5556 [=]() {
5557 _Task_generator_oversubscriber_t _Oversubscriber;
5558 (_Oversubscriber);
5559 _Func(_Progress);
5560 },
5561 _taskOptinos);
5562 }
5563
5564 template<typename _Function, typename _ProgressObject>
5565 static task<void> _GenerateTask_2PC(const _Function& _Func,
5566 const _ProgressObject& _Progress,
5567 cancellation_token_source _Cts,
5568 const _TaskCreationCallstack& _callstack)
5569 {
5570 task_options _taskOptinos(_Cts.get_token());
5571 details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack);
5572 return task<void>(
5573 [=]() {
5574 _Task_generator_oversubscriber_t _Oversubscriber;
5575 (_Oversubscriber);
5576 _Func(_Progress, _Cts.get_token());
5577 },
5578 _taskOptinos);
5579 }
5580 };
5581
5582 //
5583 // Functor returns a task - the task can directly be returned:
5584 //
5585 template<typename _ReturnType>
5586 struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, _ReturnType>
5587 {
5588 template<typename _Function>
5589 static task<_ReturnType> _GenerateTask_0(const _Function& _Func,
5590 cancellation_token_source _Cts,
5591 const _TaskCreationCallstack& _callstack)
5592 {
5593 return _Func();
5594 }
5595
5596 template<typename _Function>
5597 static task<_ReturnType> _GenerateTask_1C(const _Function& _Func,
5598 cancellation_token_source _Cts,
5599 const _TaskCreationCallstack& _callstack)
5600 {
5601 return _Func(_Cts.get_token());
5602 }
5603
5604 template<typename _Function, typename _ProgressObject>
5605 static task<_ReturnType> _GenerateTask_1P(const _Function& _Func,
5606 const _ProgressObject& _Progress,
5607 cancellation_token_source _Cts,
5608 const _TaskCreationCallstack& _callstack)
5609 {
5610 return _Func(_Progress);
5611 }
5612
5613 template<typename _Function, typename _ProgressObject>
5614 static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func,
5615 const _ProgressObject& _Progress,
5616 cancellation_token_source _Cts,
5617 const _TaskCreationCallstack& _callstack)
5618 {
5619 return _Func(_Progress, _Cts.get_token());
5620 }
5621 };
5622
5623 template<>
5624 struct _SelectorTaskGenerator<_TypeSelectorAsyncTask, void>
5625 {
5626 template<typename _Function>
5627 static task<void> _GenerateTask_0(const _Function& _Func,
5628 cancellation_token_source _Cts,
5629 const _TaskCreationCallstack& _callstack)
5630 {
5631 return _Func();
5632 }
5633
5634 template<typename _Function>
5635 static task<void> _GenerateTask_1C(const _Function& _Func,
5636 cancellation_token_source _Cts,
5637 const _TaskCreationCallstack& _callstack)
5638 {
5639 return _Func(_Cts.get_token());
5640 }
5641
5642 template<typename _Function, typename _ProgressObject>
5643 static task<void> _GenerateTask_1P(const _Function& _Func,
5644 const _ProgressObject& _Progress,
5645 cancellation_token_source _Cts,
5646 const _TaskCreationCallstack& _callstack)
5647 {
5648 return _Func(_Progress);
5649 }
5650
5651 template<typename _Function, typename _ProgressObject>
5652 static task<void> _GenerateTask_2PC(const _Function& _Func,
5653 const _ProgressObject& _Progress,
5654 cancellation_token_source _Cts,
5655 const _TaskCreationCallstack& _callstack)
5656 {
5657 return _Func(_Progress, _Cts.get_token());
5658 }
5659 };
5660
5661 template<typename _Generator, bool _TakesToken, bool TakesProgress>
5662 struct _TaskGenerator
5663 {
5664 };
5665
5666 template<typename _Generator>
5667 struct _TaskGenerator<_Generator, false, false>
5668 {
5669 template<typename _Function, typename _ClassPtr, typename _ProgressType>
5670 static auto _GenerateTask(const _Function& _Func,
5671 _ClassPtr _Ptr,
5672 cancellation_token_source _Cts,
5673 const _TaskCreationCallstack& _callstack)
5674 -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))
5675 {
5676 return _Generator::_GenerateTask_0(_Func, _Cts, _callstack);
5677 }
5678 };
5679
5680 template<typename _Generator>
5681 struct _TaskGenerator<_Generator, true, false>
5682 {
5683 template<typename _Function, typename _ClassPtr, typename _ProgressType>
5684 static auto _GenerateTask(const _Function& _Func,
5685 _ClassPtr _Ptr,
5686 cancellation_token_source _Cts,
5687 const _TaskCreationCallstack& _callstack)
5688 -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))
5689 {
5690 return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack);
5691 }
5692 };
5693
5694 template<typename _Generator>
5695 struct _TaskGenerator<_Generator, false, true>
5696 {
5697 template<typename _Function, typename _ClassPtr, typename _ProgressType>
5698 static auto _GenerateTask(const _Function& _Func,
5699 _ClassPtr _Ptr,
5700 cancellation_token_source _Cts,
5701 const _TaskCreationCallstack& _callstack)
5702 -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))
5703 {
5704 return _Generator::_GenerateTask_1P(
5705 _Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack);
5706 }
5707 };
5708
5709 template<typename _Generator>
5710 struct _TaskGenerator<_Generator, true, true>
5711 {
5712 template<typename _Function, typename _ClassPtr, typename _ProgressType>
5713 static auto _GenerateTask(const _Function& _Func,
5714 _ClassPtr _Ptr,
5715 cancellation_token_source _Cts,
5716 const _TaskCreationCallstack& _callstack)
5717 -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack))
5718 {
5719 return _Generator::_GenerateTask_2PC(
5720 _Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack);
5721 }
5722 };
5723
5724 // ***************************************************************************
5725 // Async Operation Attributes Classes
5726 //
5727 // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given
5728 // async construct in a single container. An attribute class must define:
5729 //
5730 // Mandatory:
5731 // -------------------------
5732 //
5733 // _AsyncBaseType : The Windows Runtime interface which is being implemented.
5734 // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface.
5735 // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface.
5736 // If it is false, an empty Windows Runtime type. _ReturnType : The return type of the async construct
5737 // (void for actions / non-void for operations)
5738 //
5739 // _TakesProgress : An indication as to whether or not
5740 //
5741 // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate
5742 // task
5743 //
5744 // Optional:
5745 // -------------------------
5746 //
5747
5748 template<typename _Function,
5749 typename _ProgressType,
5750 typename _ReturnType,
5751 typename _TaskTraits,
5752 bool _TakesToken,
5753 bool _TakesProgress>
5754 struct _AsyncAttributes
5755 {
5756 };
5757
5758 template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>
5759 struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true>
5760 {
5761 typedef typename Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType;
5762 typedef typename Windows::Foundation::AsyncOperationProgressHandler<_ReturnType, _ProgressType>
5763 _ProgressDelegateType;
5764 typedef typename Windows::Foundation::AsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType>
5765 _CompletionDelegateType;
5766 typedef typename _ReturnType _ReturnType;
5767 typedef typename _ProgressType _ProgressType;
5768 typedef typename _TaskTraits::_AsyncKind _AsyncKind;
5769 typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;
5770 typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator;
5771
5772 static const bool _TakesProgress = true;
5773 static const bool _TakesToken = _TakesToken;
5774
5775 template<typename _Function, typename _ClassPtr>
5776 static task<_ReturnType> _Generate_Task(const _Function& _Func,
5777 _ClassPtr _Ptr,
5778 cancellation_token_source _Cts,
5779 const _TaskCreationCallstack& _callstack)
5780 {
5781 return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);
5782 }
5783 };
5784
5785 template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>
5786 struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false>
5787 {
5788 typedef typename Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType;
5789 typedef _Zip _ProgressDelegateType;
5790 typedef typename Windows::Foundation::AsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType;
5791 typedef typename _ReturnType _ReturnType;
5792 typedef typename _TaskTraits::_AsyncKind _AsyncKind;
5793 typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;
5794 typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator;
5795
5796 static const bool _TakesProgress = false;
5797 static const bool _TakesToken = _TakesToken;
5798
5799 template<typename _Function, typename _ClassPtr>
5800 static task<_ReturnType> _Generate_Task(const _Function& _Func,
5801 _ClassPtr _Ptr,
5802 cancellation_token_source _Cts,
5803 const _TaskCreationCallstack& _callstack)
5804 {
5805 return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);
5806 }
5807 };
5808
5809 template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>
5810 struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true>
5811 {
5812 typedef typename Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType;
5813 typedef typename Windows::Foundation::AsyncActionProgressHandler<_ProgressType> _ProgressDelegateType;
5814 typedef typename Windows::Foundation::AsyncActionWithProgressCompletedHandler<_ProgressType>
5815 _CompletionDelegateType;
5816 typedef void _ReturnType;
5817 typedef typename _ProgressType _ProgressType;
5818 typedef typename _TaskTraits::_AsyncKind _AsyncKind;
5819 typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;
5820 typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator;
5821
5822 static const bool _TakesProgress = true;
5823 static const bool _TakesToken = _TakesToken;
5824
5825 template<typename _Function, typename _ClassPtr>
5826 static task<_ReturnType> _Generate_Task(const _Function& _Func,
5827 _ClassPtr _Ptr,
5828 cancellation_token_source _Cts,
5829 const _TaskCreationCallstack& _callstack)
5830 {
5831 return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);
5832 }
5833 };
5834
5835 template<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>
5836 struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false>
5837 {
5838 typedef typename Windows::Foundation::IAsyncAction _AsyncBaseType;
5839 typedef _Zip _ProgressDelegateType;
5840 typedef typename Windows::Foundation::AsyncActionCompletedHandler _CompletionDelegateType;
5841 typedef void _ReturnType;
5842 typedef typename _TaskTraits::_AsyncKind _AsyncKind;
5843 typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator;
5844 typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator;
5845
5846 static const bool _TakesProgress = false;
5847 static const bool _TakesToken = _TakesToken;
5848
5849 template<typename _Function, typename _ClassPtr>
5850 static task<_ReturnType> _Generate_Task(const _Function& _Func,
5851 _ClassPtr _Ptr,
5852 cancellation_token_source _Cts,
5853 const _TaskCreationCallstack& _callstack)
5854 {
5855 return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack);
5856 }
5857 };
5858
5859 template<typename _Function>
5860 struct _AsyncLambdaTypeTraits
5861 {
5862 typedef typename _FunctorTypeTraits<_Function>::_ReturnType _ReturnType;
5863 typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type;
5864 typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType;
5865
5866 static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress;
5867 static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken;
5868
5869 typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits;
5870 typedef typename _AsyncAttributes<_Function,
5871 _ProgressType,
5872 typename _TaskTraits::_TaskRetType,
5873 _TaskTraits,
5874 _TakesToken,
5875 _TakesProgress>
5876 _AsyncAttributes;
5877 };
5878
5879 // ***************************************************************************
5880 // AsyncInfo (and completion) Layer:
5881 //
5882
5883 //
5884 // Internal base class implementation for async operations (based on internal Windows representation for ABI level async
5885 // operations)
5886 //
5887 template<typename _Attributes, _AsyncResultType resultType = SingleResult>
5888 ref class _AsyncInfoBase abstract : _Attributes::_AsyncBaseType
5889 {
5890 internal :
5891
5892 _AsyncInfoBase()
5893 : _M_currentStatus(_AsyncStatusInternal::_AsyncCreated)
5894 , _M_errorCode(S_OK)
5895 , _M_completeDelegate(nullptr)
5896 , _M_CompleteDelegateAssigned(0)
5897 , _M_CallbackMade(0)
5898 {
5899 _M_id = ::pplx::details::platform::GetNextAsyncId();
5900 }
5901
5902 public:
5903 virtual typename _Attributes::_ReturnType GetResults()
5904 {
5905 throw ::Platform::Exception::CreateException(E_UNEXPECTED);
5906 }
5907
5908 virtual property unsigned int Id
5909 {
5910 unsigned int get()
5911 {
5912 _CheckValidStateForAsyncInfoCall();
5913
5914 return _M_id;
5915 }
5916
5917 void set(unsigned int id)
5918 {
5919 _CheckValidStateForAsyncInfoCall();
5920
5921 if (id == 0)
5922 {
5923 throw ::Platform::Exception::CreateException(E_INVALIDARG);
5924 }
5925 else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated)
5926 {
5927 throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);
5928 }
5929
5930 _M_id = id;
5931 }
5932 }
5933
5934 virtual property Windows::Foundation::AsyncStatus Status
5935 {
5936 Windows::Foundation::AsyncStatus get()
5937 {
5938 _CheckValidStateForAsyncInfoCall();
5939
5940 _AsyncStatusInternal _Current = _M_currentStatus;
5941
5942 //
5943 // Map our internal cancel pending to canceled. This way "pending canceled" looks to the outside as
5944 // "canceled" but can still transition to "completed" if the operation completes without acknowledging the
5945 // cancellation request
5946 //
5947 switch (_Current)
5948 {
5949 case _AsyncCancelPending: _Current = _AsyncCanceled; break;
5950 case _AsyncCreated: _Current = _AsyncStarted; break;
5951 default: break;
5952 }
5953
5954 return static_cast<Windows::Foundation::AsyncStatus>(_Current);
5955 }
5956 }
5957
5958 virtual property Windows::Foundation::HResult ErrorCode
5959 {
5960 Windows::Foundation::HResult get()
5961 {
5962 _CheckValidStateForAsyncInfoCall();
5963
5964 Windows::Foundation::HResult _Hr;
5965 _Hr.Value = _M_errorCode;
5966 return _Hr;
5967 }
5968 }
5969
5970 virtual property typename _Attributes::_ProgressDelegateType ^
5971 Progress {
5972 typename typename _Attributes::_ProgressDelegateType ^ get() { return _GetOnProgress(); }
5973
5974 void set(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler)
5975 {
5976 _PutOnProgress(_ProgressHandler);
5977 }
5978 }
5979
5980 virtual void
5981 Cancel()
5982 {
5983 if (_TransitionToState(_AsyncCancelPending))
5984 {
5985 _OnCancel();
5986 }
5987 }
5988
5989 virtual void Close()
5990 {
5991 if (_TransitionToState(_AsyncClosed))
5992 {
5993 _OnClose();
5994 }
5995 else
5996 {
5997 if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored
5998 {
5999 throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE);
6000 }
6001 }
6002 }
6003
6004 virtual property typename _Attributes::_CompletionDelegateType ^
6005 Completed {
6006 typename _Attributes::_CompletionDelegateType ^
6007 get() {
6008 _CheckValidStateForDelegateCall();
6009 return _M_completeDelegate;
6010 }
6011
6012 void set(typename _Attributes::_CompletionDelegateType ^ _CompleteHandler)
6013 {
6014 _CheckValidStateForDelegateCall();
6015 // this delegate property is "write once"
6016 if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1)
6017 {
6018 _M_completeDelegateContext = _ContextCallback::_CaptureCurrent();
6019 _M_completeDelegate = _CompleteHandler;
6020 // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state
6021 // below as perceived from _FireCompletion on another thread.
6022 MemoryBarrier();
6023 if (_IsTerminalState())
6024 {
6025 _FireCompletion();
6026 }
6027 }
6028 else
6029 {
6030 throw ::Platform::Exception::CreateException(E_ILLEGAL_DELEGATE_ASSIGNMENT);
6031 }
6032 }
6033 }
6034
6035 protected private :
6036
6037 // _Start - this is not externally visible since async operations "hot start" before returning to the caller
6038 void
6039 _Start()
6040 {
6041 if (_TransitionToState(_AsyncStarted))
6042 {
6043 _OnStart();
6044 }
6045 else
6046 {
6047 throw ::Platform::Exception::CreateException(E_ILLEGAL_STATE_CHANGE);
6048 }
6049 }
6050
6051 void _FireCompletion()
6052 {
6053 _TryTransitionToCompleted();
6054
6055 // we guarantee that completion can only ever be fired once
6056 if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1)
6057 {
6058 _M_completeDelegateContext._CallInContext([=] {
6059 _M_completeDelegate((_Attributes::_AsyncBaseType ^) this, this->Status);
6060 _M_completeDelegate = nullptr;
6061 });
6062 }
6063 }
6064
6065 virtual typename _Attributes::_ProgressDelegateType ^
6066 _GetOnProgress() { throw ::Platform::Exception::CreateException(E_UNEXPECTED); }
6067
6068 virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler)
6069 {
6070 throw ::Platform::Exception::CreateException(E_UNEXPECTED);
6071 }
6072
6073 bool _TryTransitionToCompleted() { return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); }
6074
6075 bool _TryTransitionToCancelled() { return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); }
6076
6077 bool _TryTransitionToError(const HRESULT error)
6078 {
6079 _InterlockedCompareExchange(reinterpret_cast<volatile LONG*>(&_M_errorCode), error, S_OK);
6080 return _TransitionToState(_AsyncStatusInternal::_AsyncError);
6081 }
6082
6083 // This method checks to see if the delegate properties can be
6084 // modified in the current state and generates the appropriate
6085 // error hr in the case of violation.
6086 inline void _CheckValidStateForDelegateCall()
6087 {
6088 if (_M_currentStatus == _AsyncClosed)
6089 {
6090 throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);
6091 }
6092 }
6093
6094 // This method checks to see if results can be collected in the
6095 // current state and generates the appropriate error hr in
6096 // the case of a violation.
6097 inline void _CheckValidStateForResultsCall()
6098 {
6099 _AsyncStatusInternal _Current = _M_currentStatus;
6100
6101 if (_Current == _AsyncError)
6102 {
6103 throw ::Platform::Exception::CreateException(_M_errorCode);
6104 }
6105 #pragma warning(push)
6106 #pragma warning(disable : 4127) // Conditional expression is constant
6107 // single result illegal before transition to Completed or Cancelled state
6108 if (resultType == SingleResult)
6109 #pragma warning(pop)
6110 {
6111 if (_Current != _AsyncCompleted)
6112 {
6113 throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);
6114 }
6115 }
6116 // multiple results can be called after Start has been called and before/after Completed
6117 else if (_Current != _AsyncStarted && _Current != _AsyncCancelPending && _Current != _AsyncCanceled &&
6118 _Current != _AsyncCompleted)
6119 {
6120 throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);
6121 }
6122 }
6123
6124 // This method can be called by derived classes periodically to determine
6125 // whether the asynchronous operation should continue processing or should
6126 // be halted.
6127 inline bool _ContinueAsyncOperation() { return (_M_currentStatus == _AsyncStarted); }
6128
6129 // These two methods are used to allow the async worker implementation do work on
6130 // state transitions. No real "work" should be done in these methods. In other words
6131 // they should not block for a long time on UI timescales.
6132 virtual void _OnStart() = 0;
6133 virtual void _OnClose() = 0;
6134 virtual void _OnCancel() = 0;
6135
6136 private:
6137 // This method is used to check if calls to the AsyncInfo properties
6138 // (id, status, errorcode) are legal in the current state. It also
6139 // generates the appropriate error hr to return in the case of an
6140 // illegal call.
6141 inline void _CheckValidStateForAsyncInfoCall()
6142 {
6143 _AsyncStatusInternal _Current = _M_currentStatus;
6144 if (_Current == _AsyncClosed)
6145 {
6146 throw ::Platform::Exception::CreateException(E_ILLEGAL_METHOD_CALL);
6147 }
6148 else if (_Current == _AsyncCreated)
6149 {
6150 throw ::Platform::Exception::CreateException(E_ASYNC_OPERATION_NOT_STARTED);
6151 }
6152 }
6153
6154 inline bool _TransitionToState(const _AsyncStatusInternal _NewState)
6155 {
6156 _AsyncStatusInternal _Current = _M_currentStatus;
6157
6158 // This enforces the valid state transitions of the asynchronous worker object
6159 // state machine.
6160 switch (_NewState)
6161 {
6162 case _AsyncStatusInternal::_AsyncStarted:
6163 if (_Current != _AsyncCreated)
6164 {
6165 return false;
6166 }
6167 break;
6168 case _AsyncStatusInternal::_AsyncCompleted:
6169 if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)
6170 {
6171 return false;
6172 }
6173 break;
6174 case _AsyncStatusInternal::_AsyncCancelPending:
6175 if (_Current != _AsyncStarted)
6176 {
6177 return false;
6178 }
6179 break;
6180 case _AsyncStatusInternal::_AsyncCanceled:
6181 if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)
6182 {
6183 return false;
6184 }
6185 break;
6186 case _AsyncStatusInternal::_AsyncError:
6187 if (_Current != _AsyncStarted && _Current != _AsyncCancelPending)
6188 {
6189 return false;
6190 }
6191 break;
6192 case _AsyncStatusInternal::_AsyncClosed:
6193 if (!_IsTerminalState(_Current))
6194 {
6195 return false;
6196 }
6197 break;
6198 default: return false; break;
6199 }
6200
6201 // attempt the transition to the new state
6202 // Note: if currentStatus_ == _Current, then there was no intervening write
6203 // by the async work object and the swap succeeded.
6204 _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>(_InterlockedCompareExchange(
6205 reinterpret_cast<volatile LONG*>(&_M_currentStatus), _NewState, static_cast<LONG>(_Current)));
6206
6207 // ICE returns the former state, if the returned state and the
6208 // state we captured at the beginning of this method are the same,
6209 // the swap succeeded.
6210 return (_RetState == _Current);
6211 }
6212
6213 inline bool _IsTerminalState() { return _IsTerminalState(_M_currentStatus); }
6214
6215 inline bool _IsTerminalState(_AsyncStatusInternal status)
6216 {
6217 return (status == _AsyncError || status == _AsyncCanceled || status == _AsyncCompleted ||
6218 status == _AsyncClosed);
6219 }
6220
6221 private:
6222 _ContextCallback _M_completeDelegateContext;
6223 typename _Attributes::_CompletionDelegateType ^ volatile _M_completeDelegate;
6224 _AsyncStatusInternal volatile _M_currentStatus;
6225 HRESULT volatile _M_errorCode;
6226 unsigned int _M_id;
6227 long volatile _M_CompleteDelegateAssigned;
6228 long volatile _M_CallbackMade;
6229 };
6230
6231 // ***************************************************************************
6232 // Progress Layer (optional):
6233 //
6234
6235 template<typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult>
6236 ref class _AsyncProgressBase abstract : _AsyncInfoBase<_Attributes, _ResultType>
6237 {
6238 };
6239
6240 template<typename _Attributes, _AsyncResultType _ResultType>
6241 ref class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : _AsyncInfoBase<_Attributes, _ResultType>
6242 {
6243 internal :
6244
6245 _AsyncProgressBase()
6246 : _AsyncInfoBase<_Attributes, _ResultType>(), _M_progressDelegate(nullptr)
6247 {
6248 }
6249
6250 virtual typename _Attributes::_ProgressDelegateType ^ _GetOnProgress() override
6251 {
6252 _CheckValidStateForDelegateCall();
6253 return _M_progressDelegate;
6254 }
6255
6256 virtual void _PutOnProgress(typename _Attributes::_ProgressDelegateType ^ _ProgressHandler) override
6257 {
6258 _CheckValidStateForDelegateCall();
6259 _M_progressDelegate = _ProgressHandler;
6260 _M_progressDelegateContext = _ContextCallback::_CaptureCurrent();
6261 }
6262
6263 void _FireProgress(const typename _Attributes::_ProgressType& _ProgressValue)
6264 {
6265 if (_M_progressDelegate != nullptr)
6266 {
6267 _M_progressDelegateContext._CallInContext(
6268 [=] { _M_progressDelegate((_Attributes::_AsyncBaseType ^) this, _ProgressValue); });
6269 }
6270 }
6271
6272 private:
6273 _ContextCallback _M_progressDelegateContext;
6274 typename _Attributes::_ProgressDelegateType ^ _M_progressDelegate;
6275 };
6276
6277 template<typename _Attributes, _AsyncResultType _ResultType = SingleResult>
6278 ref class _AsyncBaseProgressLayer abstract : _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType>
6279 {
6280 };
6281
6282 // ***************************************************************************
6283 // Task Adaptation Layer:
6284 //
6285
6286 //
6287 // _AsyncTaskThunkBase provides a bridge between IAsync<Action/Operation> and task.
6288 //
6289 template<typename _Attributes, typename _ReturnType>
6290 ref class _AsyncTaskThunkBase abstract : _AsyncBaseProgressLayer<_Attributes>
6291 {
6292 public:
6293 virtual _ReturnType GetResults() override
6294 {
6295 _CheckValidStateForResultsCall();
6296 return _M_task.get();
6297 }
6298
6299 internal :
6300
6301 typedef task<_ReturnType>
6302 _TaskType;
6303
6304 _AsyncTaskThunkBase(const _TaskType& _Task) : _M_task(_Task) {}
6305
6306 _AsyncTaskThunkBase() {}
6307
6308 protected:
6309 virtual void _OnStart() override
6310 {
6311 _M_task.then([=](_TaskType _Antecedent) {
6312 try
6313 {
6314 _Antecedent.get();
6315 }
6316 catch (task_canceled&)
6317 {
6318 _TryTransitionToCancelled();
6319 }
6320 catch (::Platform::Exception ^ _Ex)
6321 {
6322 _TryTransitionToError(_Ex->HResult);
6323 }
6324 catch (...)
6325 {
6326 _TryTransitionToError(E_FAIL);
6327 }
6328 _FireCompletion();
6329 });
6330 }
6331
6332 internal :
6333
6334 _TaskType _M_task;
6335 cancellation_token_source _M_cts;
6336 };
6337
6338 template<typename _Attributes>
6339 ref class _AsyncTaskThunk : _AsyncTaskThunkBase<_Attributes, typename _Attributes::_ReturnType>
6340 {
6341 internal :
6342
6343 _AsyncTaskThunk(const _TaskType& _Task)
6344 : _AsyncTaskThunkBase(_Task)
6345 {
6346 }
6347
6348 _AsyncTaskThunk() {}
6349
6350 protected:
6351 virtual void _OnClose() override {}
6352
6353 virtual void _OnCancel() override { _M_cts.cancel(); }
6354 };
6355
6356 // ***************************************************************************
6357 // Async Creation Layer:
6358 //
6359 template<typename _Function>
6360 ref class _AsyncTaskGeneratorThunk sealed
6361 : _AsyncTaskThunk<typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes>
6362 {
6363 internal :
6364
6365 typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes;
6366 typedef typename _AsyncTaskThunk<_Attributes> _Base;
6367 typedef typename _Attributes::_AsyncBaseType _AsyncBaseType;
6368
6369 _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack& _callstack)
6370 : _M_func(_Func), _M_creationCallstack(_callstack)
6371 {
6372 // Virtual call here is safe as the class is declared 'sealed'
6373 _Start();
6374 }
6375
6376 protected:
6377 //
6378 // The only thing we must do different from the base class is we must spin the hot task on transition from
6379 // Created->Started. Otherwise, let the base thunk handle everything.
6380 //
6381
6382 virtual void _OnStart() override
6383 {
6384 //
6385 // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the
6386 // user lambda for progress reports, wrap the return result in a task, or allow for direct return of a task
6387 // depending on the form of the lambda.
6388 //
6389 _M_task = _Attributes::_Generate_Task(_M_func, this, _M_cts, _M_creationCallstack);
6390 _Base::_OnStart();
6391 }
6392
6393 virtual void _OnCancel() override { _Base::_OnCancel(); }
6394
6395 private:
6396 _TaskCreationCallstack _M_creationCallstack;
6397 _Function _M_func;
6398 };
6399 } // namespace details
6400
6401 /// <summary>
6402 /// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return
6403 /// type of <c>create_async</c> is one of either <c>IAsyncAction^</c>,
6404 /// <c>IAsyncActionWithProgress<TProgress>^</c>, <c>IAsyncOperation<TResult>^</c>, or
6405 /// <c>IAsyncOperationWithProgress<TResult, TProgress>^</c> based on the signature of the lambda passed to the
6406 /// method.
6407 /// </summary>
6408 /// <param name="_Func">
6409 /// The lambda or function object from which to create a Windows Runtime asynchronous construct.
6410 /// </param>
6411 /// <returns>
6412 /// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^,
6413 /// IAsyncOperation<TResult>^, or an IAsyncOperationWithProgress<TResult, TProgress>^. The interface
6414 /// returned depends on the signature of the lambda passed into the function.
6415 /// </returns>
6416 /// <remarks>
6417 /// The return type of the lambda determines whether the construct is an action or an operation.
6418 /// <para>Lambdas that return void cause the creation of actions. Lambdas that return a result of type
6419 /// <c>TResult</c> cause the creation of operations of TResult.</para> <para>The lambda may also return a
6420 /// <c>task<TResult></c> which encapsulates the asynchronous work within itself or is the continuation of a
6421 /// chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since
6422 /// the tasks are the ones that execute asynchronously, and the return type of the lambda is unwrapped to produce
6423 /// the asynchronous construct returned by <c>create_async</c>. This implies that a lambda that returns a
6424 /// task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will cause
6425 /// the creation of operations of TResult.</para> <para>The lambda may take either zero, one or two arguments. The
6426 /// valid arguments are <c>progress_reporter<TProgress></c> and <c>cancellation_token</c>, in that order if
6427 /// both are used. A lambda without arguments causes the creation of an asynchronous construct without the
6428 /// capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause
6429 /// <c>create_async</c> to return an asynchronous construct which reports progress of type TProgress each time the
6430 /// <c>report</c> method of the progress_reporter object is called. A lambda that takes a cancellation_token may use
6431 /// that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the
6432 /// asynchronous construct causes cancellation of those tasks.</para>
6433 /// <para>If the body of the lambda or function object returns a result (and not a task<TResult>), the lambda
6434 /// will be executed asynchronously within the process MTA in the context of a task the Runtime implicitly creates
6435 /// for it. The <c>IAsyncInfo::Cancel</c> method will cause cancellation of the implicit task.</para> <para>If the
6436 /// body of the lambda returns a task, the lambda executes inline, and by declaring the lambda to take an argument
6437 /// of type <c>cancellation_token</c> you can trigger cancellation of any tasks you create within the lambda by
6438 /// passing that token in when you create them. You may also use the <c>register_callback</c> method on the token to
6439 /// cause the Runtime to invoke a callback when you call <c>IAsyncInfo::Cancel</c> on the async operation or action
6440 /// produced..</para> <para>This function is only available to Windows Store apps.</para>
6441 /// </remarks>
6442 /// <seealso cref="task Class"/>
6443 /// <seealso cref="progress_reporter Class"/>
6444 /// <seealso cref="cancelation_token Class"/>
6445 /**/
6446 template<typename _Function>
6447 __declspec(noinline) details::_AsyncTaskGeneratorThunk<_Function> ^
6448 create_async(const _Function& _Func) {
6449 static_assert(std::is_same<decltype(details::_IsValidCreateAsync(_Func, 0, 0, 0, 0)), std::true_type>::value,
6450 "argument to create_async must be a callable object taking zero, one or two arguments");
6451 return ref new details::_AsyncTaskGeneratorThunk<_Function>(_Func, PPLX_CAPTURE_CALLSTACK());
6452 }
6453
6454 #endif /* defined (__cplusplus_winrt) */
6455
6456 namespace details
6457 {
6458 // Helper struct for when_all operators to know when tasks have completed
6459 template<typename _Type>
6460 struct _RunAllParam
6461 {
6462 _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}
6463
6464 void _Resize(size_t _Len, bool _SkipVector = false)
6465 {
6466 _M_numTasks = _Len;
6467 if (!_SkipVector)
6468 {
6469 _M_vector._Result.resize(_Len);
6470 }
6471 }
6472
6473 task_completion_event<_Unit_type> _M_completed;
6474 _ResultHolder<std::vector<_Type>> _M_vector;
6475 _ResultHolder<_Type> _M_mergeVal;
6476 atomic_size_t _M_completeCount;
6477 size_t _M_numTasks;
6478 };
6479
6480 template<typename _Type>
6481 struct _RunAllParam<std::vector<_Type>>
6482 {
6483 _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}
6484
6485 void _Resize(size_t _Len, bool _SkipVector = false)
6486 {
6487 _M_numTasks = _Len;
6488
6489 if (!_SkipVector)
6490 {
6491 _M_vector.resize(_Len);
6492 }
6493 }
6494
6495 task_completion_event<_Unit_type> _M_completed;
6496 std::vector<_ResultHolder<std::vector<_Type>>> _M_vector;
6497 atomic_size_t _M_completeCount;
6498 size_t _M_numTasks;
6499 };
6500
6501 // Helper struct specialization for void
6502 template<>
6503 struct _RunAllParam<_Unit_type>
6504 {
6505 _RunAllParam() : _M_completeCount(0), _M_numTasks(0) {}
6506
6507 void _Resize(size_t _Len) { _M_numTasks = _Len; }
6508
6509 task_completion_event<_Unit_type> _M_completed;
6510 atomic_size_t _M_completeCount;
6511 size_t _M_numTasks;
6512 };
6513
6514 inline void _JoinAllTokens_Add(const cancellation_token_source& _MergedSrc,
6515 _CancellationTokenState* _PJoinedTokenState)
6516 {
6517 if (_PJoinedTokenState != nullptr && _PJoinedTokenState != _CancellationTokenState::_None())
6518 {
6519 cancellation_token _T = cancellation_token::_FromImpl(_PJoinedTokenState);
6520 _T.register_callback([=]() { _MergedSrc.cancel(); });
6521 }
6522 }
6523
6524 template<typename _ElementType, typename _Function, typename _TaskType>
6525 void _WhenAllContinuationWrapper(_RunAllParam<_ElementType> * _PParam, _Function _Func, task<_TaskType> & _Task)
6526 {
6527 if (_Task._GetImpl()->_IsCompleted())
6528 {
6529 _Func();
6530 if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)
6531 {
6532 // Inline execute its direct continuation, the _ReturnTask
6533 _PParam->_M_completed.set(_Unit_type());
6534 // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished.
6535 delete _PParam;
6536 }
6537 }
6538 else
6539 {
6540 _ASSERTE(_Task._GetImpl()->_IsCanceled());
6541 if (_Task._GetImpl()->_HasUserException())
6542 {
6543 // _Cancel will return false if the TCE is already canceled with or without exception
6544 _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder());
6545 }
6546 else
6547 {
6548 _PParam->_M_completed._Cancel();
6549 }
6550
6551 if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)
6552 {
6553 delete _PParam;
6554 }
6555 }
6556 }
6557
6558 template<typename _ElementType, typename _Iterator>
6559 struct _WhenAllImpl
6560 {
6561 static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions,
6562 _Iterator _Begin,
6563 _Iterator _End)
6564 {
6565 _CancellationTokenState* _PTokenState =
6566 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
6567
6568 auto _PParam = new _RunAllParam<_ElementType>();
6569 cancellation_token_source _MergedSource;
6570
6571 // Step1: Create task completion event.
6572 task_options _Options(_TaskOptions);
6573 _Options.set_cancellation_token(_MergedSource.get_token());
6574 task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);
6575 // The return task must be created before step 3 to enforce inline execution.
6576 auto _ReturnTask = _All_tasks_completed._Then(
6577 [=](_Unit_type) -> std::vector<_ElementType> { return _PParam->_M_vector.Get(); }, nullptr);
6578
6579 // Step2: Combine and check tokens, and count elements in range.
6580 if (_PTokenState)
6581 {
6582 _JoinAllTokens_Add(_MergedSource, _PTokenState);
6583 _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));
6584 }
6585 else
6586 {
6587 size_t _TaskNum = 0;
6588 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6589 {
6590 _TaskNum++;
6591 _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);
6592 }
6593 _PParam->_Resize(_TaskNum);
6594 }
6595
6596 // Step3: Check states of previous tasks.
6597 if (_Begin == _End)
6598 {
6599 _PParam->_M_completed.set(_Unit_type());
6600 delete _PParam;
6601 }
6602 else
6603 {
6604 size_t _Index = 0;
6605 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6606 {
6607 if (_PTask->is_apartment_aware())
6608 {
6609 _ReturnTask._SetAsync();
6610 }
6611
6612 _PTask->_Then(
6613 [_PParam, _Index](task<_ElementType> _ResultTask) {
6614 auto _PParamCopy = _PParam;
6615 auto _IndexCopy = _Index;
6616 auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() {
6617 _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult();
6618 };
6619
6620 _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);
6621 },
6622 _CancellationTokenState::_None());
6623
6624 _Index++;
6625 }
6626 }
6627
6628 return _ReturnTask;
6629 }
6630 };
6631
6632 template<typename _ElementType, typename _Iterator>
6633 struct _WhenAllImpl<std::vector<_ElementType>, _Iterator>
6634 {
6635 static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions,
6636 _Iterator _Begin,
6637 _Iterator _End)
6638 {
6639 _CancellationTokenState* _PTokenState =
6640 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
6641
6642 auto _PParam = new _RunAllParam<std::vector<_ElementType>>();
6643 cancellation_token_source _MergedSource;
6644
6645 // Step1: Create task completion event.
6646 task_options _Options(_TaskOptions);
6647 _Options.set_cancellation_token(_MergedSource.get_token());
6648 task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);
6649 // The return task must be created before step 3 to enforce inline execution.
6650 auto _ReturnTask = _All_tasks_completed._Then(
6651 [=](_Unit_type) -> std::vector<_ElementType> {
6652 _ASSERTE(_PParam->_M_completeCount == _PParam->_M_numTasks);
6653 std::vector<_ElementType> _Result;
6654 for (size_t _I = 0; _I < _PParam->_M_numTasks; _I++)
6655 {
6656 const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get();
6657 _Result.insert(_Result.end(), _Vec.begin(), _Vec.end());
6658 }
6659 return _Result;
6660 },
6661 nullptr);
6662
6663 // Step2: Combine and check tokens, and count elements in range.
6664 if (_PTokenState)
6665 {
6666 _JoinAllTokens_Add(_MergedSource, _PTokenState);
6667 _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));
6668 }
6669 else
6670 {
6671 size_t _TaskNum = 0;
6672 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6673 {
6674 _TaskNum++;
6675 _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);
6676 }
6677 _PParam->_Resize(_TaskNum);
6678 }
6679
6680 // Step3: Check states of previous tasks.
6681 if (_Begin == _End)
6682 {
6683 _PParam->_M_completed.set(_Unit_type());
6684 delete _PParam;
6685 }
6686 else
6687 {
6688 size_t _Index = 0;
6689 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6690 {
6691 if (_PTask->is_apartment_aware())
6692 {
6693 _ReturnTask._SetAsync();
6694 }
6695
6696 _PTask->_Then(
6697 [_PParam, _Index](task<std::vector<_ElementType>> _ResultTask) {
6698 auto _PParamCopy = _PParam;
6699 auto _IndexCopy = _Index;
6700 auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() {
6701 _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult());
6702 };
6703
6704 _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);
6705 },
6706 _CancellationTokenState::_None());
6707
6708 _Index++;
6709 }
6710 }
6711
6712 return _ReturnTask;
6713 }
6714 };
6715
6716 template<typename _Iterator>
6717 struct _WhenAllImpl<void, _Iterator>
6718 {
6719 static task<void> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
6720 {
6721 _CancellationTokenState* _PTokenState =
6722 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
6723
6724 auto _PParam = new _RunAllParam<_Unit_type>();
6725 cancellation_token_source _MergedSource;
6726
6727 // Step1: Create task completion event.
6728 task_options _Options(_TaskOptions);
6729 _Options.set_cancellation_token(_MergedSource.get_token());
6730 task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options);
6731 // The return task must be created before step 3 to enforce inline execution.
6732 auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) {}, nullptr);
6733
6734 // Step2: Combine and check tokens, and count elements in range.
6735 if (_PTokenState)
6736 {
6737 _JoinAllTokens_Add(_MergedSource, _PTokenState);
6738 _PParam->_Resize(static_cast<size_t>(std::distance(_Begin, _End)));
6739 }
6740 else
6741 {
6742 size_t _TaskNum = 0;
6743 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6744 {
6745 _TaskNum++;
6746 _JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState);
6747 }
6748 _PParam->_Resize(_TaskNum);
6749 }
6750
6751 // Step3: Check states of previous tasks.
6752 if (_Begin == _End)
6753 {
6754 _PParam->_M_completed.set(_Unit_type());
6755 delete _PParam;
6756 }
6757 else
6758 {
6759 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
6760 {
6761 if (_PTask->is_apartment_aware())
6762 {
6763 _ReturnTask._SetAsync();
6764 }
6765
6766 _PTask->_Then(
6767 [_PParam](task<void> _ResultTask) {
6768 auto _Func = []() {};
6769 _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);
6770 },
6771 _CancellationTokenState::_None());
6772 }
6773 }
6774
6775 return _ReturnTask;
6776 }
6777 };
6778
6779 template<typename _ReturnType>
6780 task<std::vector<_ReturnType>> _WhenAllVectorAndValue(
6781 const task<std::vector<_ReturnType>>& _VectorTask, const task<_ReturnType>& _ValueTask, bool _OutputVectorFirst)
6782 {
6783 auto _PParam = new _RunAllParam<_ReturnType>();
6784 cancellation_token_source _MergedSource;
6785
6786 // Step1: Create task completion event.
6787 task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token());
6788 // The return task must be created before step 3 to enforce inline execution.
6789 auto _ReturnTask = _All_tasks_completed._Then(
6790 [=](_Unit_type) -> std::vector<_ReturnType> {
6791 _ASSERTE(_PParam->_M_completeCount == 2);
6792 auto _Result = _PParam->_M_vector.Get(); // copy by value
6793 auto _mergeVal = _PParam->_M_mergeVal.Get();
6794
6795 if (_OutputVectorFirst == true)
6796 {
6797 _Result.push_back(_mergeVal);
6798 }
6799 else
6800 {
6801 _Result.insert(_Result.begin(), _mergeVal);
6802 }
6803 return _Result;
6804 },
6805 nullptr);
6806
6807 // Step2: Combine and check tokens.
6808 _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState);
6809 _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState);
6810
6811 // Step3: Check states of previous tasks.
6812 _PParam->_Resize(2, true);
6813
6814 if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware())
6815 {
6816 _ReturnTask._SetAsync();
6817 }
6818 _VectorTask._Then(
6819 [_PParam](task<std::vector<_ReturnType>> _ResultTask) {
6820 auto _PParamCopy = _PParam;
6821 auto _Func = [_PParamCopy, &_ResultTask]() {
6822 auto _ResultLocal = _ResultTask._GetImpl()->_GetResult();
6823 _PParamCopy->_M_vector.Set(_ResultLocal);
6824 };
6825
6826 _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);
6827 },
6828 _CancellationTokenState::_None());
6829 _ValueTask._Then(
6830 [_PParam](task<_ReturnType> _ResultTask) {
6831 auto _PParamCopy = _PParam;
6832 auto _Func = [_PParamCopy, &_ResultTask]() {
6833 auto _ResultLocal = _ResultTask._GetImpl()->_GetResult();
6834 _PParamCopy->_M_mergeVal.Set(_ResultLocal);
6835 };
6836
6837 _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask);
6838 },
6839 _CancellationTokenState::_None());
6840
6841 return _ReturnTask;
6842 }
6843 } // namespace details
6844
6845 /// <summary>
6846 /// Creates a task that will complete successfully when all of the tasks supplied as arguments complete
6847 /// successfully.
6848 /// </summary>
6849 /// <typeparam name="_Iterator">
6850 /// The type of the input iterator.
6851 /// </typeparam>
6852 /// <param name="_Begin">
6853 /// The position of the first element in the range of elements to be combined into the resulting task.
6854 /// </param>
6855 /// <param name="_End">
6856 /// The position of the first element beyond the range of elements to be combined into the resulting task.
6857 /// </param>
6858 /// <returns>
6859 /// A task that completes successfully when all of the input tasks have completed successfully. If the input tasks
6860 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T>></c>. If the
6861 /// input tasks are of type <c>void</c> the output task will also be a <c>task<void></c>.
6862 /// </returns>
6863 /// <remarks>
6864 /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled
6865 /// state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on
6866 /// that task.
6867 /// </remarks>
6868 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
6869 /**/
6870 template<typename _Iterator>
6871 auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())
6872 -> decltype(details::_WhenAllImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,
6873 _Iterator>::_Perform(_TaskOptions, _Begin, _End))
6874 {
6875 typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;
6876 return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End);
6877 }
6878
6879 /// <summary>
6880 /// Creates a task that will complete successfully when both of the tasks supplied as arguments complete
6881 /// successfully.
6882 /// </summary>
6883 /// <typeparam name="_ReturnType">
6884 /// The type of the returned task.
6885 /// </typeparam>
6886 /// <param name="_Lhs">
6887 /// The first task to combine into the resulting task.
6888 /// </param>
6889 /// <param name="_Rhs">
6890 /// The second task to combine into the resulting task.
6891 /// </param>
6892 /// <returns>
6893 /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks
6894 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T>></c>. If the
6895 /// input tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for
6896 /// a construct of the sort taskA && taskB && taskC, which are combined in pairs, the &&
6897 /// operator produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type
6898 /// <c>task<std::vector<T>></c>.</para>
6899 /// </returns>
6900 /// <remarks>
6901 /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled
6902 /// state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on
6903 /// that task.
6904 /// </remarks>
6905 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
6906 /**/
6907 template<typename _ReturnType>
6908 auto operator&&(const task<_ReturnType>& _Lhs, const task<_ReturnType>& _Rhs) -> decltype(when_all(&_Lhs, &_Lhs))
6909 {
6910 task<_ReturnType> _PTasks[2] = {_Lhs, _Rhs};
6911 return when_all(_PTasks, _PTasks + 2);
6912 }
6913
6914 /// <summary>
6915 /// Creates a task that will complete successfully when both of the tasks supplied as arguments complete
6916 /// successfully.
6917 /// </summary>
6918 /// <typeparam name="_ReturnType">
6919 /// The type of the returned task.
6920 /// </typeparam>
6921 /// <param name="_Lhs">
6922 /// The first task to combine into the resulting task.
6923 /// </param>
6924 /// <param name="_Rhs">
6925 /// The second task to combine into the resulting task.
6926 /// </param>
6927 /// <returns>
6928 /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks
6929 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T>></c>. If the
6930 /// input tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for
6931 /// a construct of the sort taskA && taskB && taskC, which are combined in pairs, the &&
6932 /// operator produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type
6933 /// <c>task<std::vector<T>></c>.</para>
6934 /// </returns>
6935 /// <remarks>
6936 /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled
6937 /// state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on
6938 /// that task.
6939 /// </remarks>
6940 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
6941 /**/
6942 template<typename _ReturnType>
6943 auto operator&&(const task<std::vector<_ReturnType>>& _Lhs, const task<_ReturnType>& _Rhs)
6944 -> decltype(details::_WhenAllVectorAndValue(_Lhs, _Rhs, true))
6945 {
6946 return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true);
6947 }
6948
6949 /// <summary>
6950 /// Creates a task that will complete successfully when both of the tasks supplied as arguments complete
6951 /// successfully.
6952 /// </summary>
6953 /// <typeparam name="_ReturnType">
6954 /// The type of the returned task.
6955 /// </typeparam>
6956 /// <param name="_Lhs">
6957 /// The first task to combine into the resulting task.
6958 /// </param>
6959 /// <param name="_Rhs">
6960 /// The second task to combine into the resulting task.
6961 /// </param>
6962 /// <returns>
6963 /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks
6964 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T>></c>. If the
6965 /// input tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for
6966 /// a construct of the sort taskA && taskB && taskC, which are combined in pairs, the &&
6967 /// operator produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type
6968 /// <c>task<std::vector<T>></c>.</para>
6969 /// </returns>
6970 /// <remarks>
6971 /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled
6972 /// state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on
6973 /// that task.
6974 /// </remarks>
6975 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
6976 /**/
6977 template<typename _ReturnType>
6978 auto operator&&(const task<_ReturnType>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs)
6979 -> decltype(details::_WhenAllVectorAndValue(_Rhs, _Lhs, false))
6980 {
6981 return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false);
6982 }
6983
6984 /// <summary>
6985 /// Creates a task that will complete successfully when both of the tasks supplied as arguments complete
6986 /// successfully.
6987 /// </summary>
6988 /// <typeparam name="_ReturnType">
6989 /// The type of the returned task.
6990 /// </typeparam>
6991 /// <param name="_Lhs">
6992 /// The first task to combine into the resulting task.
6993 /// </param>
6994 /// <param name="_Rhs">
6995 /// The second task to combine into the resulting task.
6996 /// </param>
6997 /// <returns>
6998 /// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks
6999 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T>></c>. If the
7000 /// input tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for
7001 /// a construct of the sort taskA && taskB && taskC, which are combined in pairs, the &&
7002 /// operator produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type
7003 /// <c>task<std::vector<T>></c>.</para>
7004 /// </returns>
7005 /// <remarks>
7006 /// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled
7007 /// state, and the exception, if one is encountered, will be thrown if you call <c>get()</c> or <c>wait()</c> on
7008 /// that task.
7009 /// </remarks>
7010 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7011 /**/
7012 template<typename _ReturnType>
7013 auto operator&&(const task<std::vector<_ReturnType>>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs)
7014 -> decltype(when_all(&_Lhs, &_Lhs))
7015 {
7016 task<std::vector<_ReturnType>> _PTasks[2] = {_Lhs, _Rhs};
7017 return when_all(_PTasks, _PTasks + 2);
7018 }
7019
7020 namespace details
7021 {
7022 // Helper struct for when_any operators to know when tasks have completed
7023 template<typename _CompletionType>
7024 struct _RunAnyParam
7025 {
7026 _RunAnyParam() : _M_exceptionRelatedToken(nullptr), _M_completeCount(0), _M_numTasks(0), _M_fHasExplicitToken(false)
7027 {
7028 }
7029 ~_RunAnyParam()
7030 {
7031 if (_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) _M_exceptionRelatedToken->_Release();
7032 }
7033 task_completion_event<_CompletionType> _M_Completed;
7034 cancellation_token_source _M_cancellationSource;
7035 _CancellationTokenState* _M_exceptionRelatedToken;
7036 atomic_size_t _M_completeCount;
7037 size_t _M_numTasks;
7038 bool _M_fHasExplicitToken;
7039 };
7040
7041 template<typename _CompletionType, typename _Function, typename _TaskType>
7042 void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType>* _PParam, const _Function& _Func, task<_TaskType>& _Task)
7043 {
7044 bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken &&
7045 _Task._GetImpl()->_M_pTokenState != _CancellationTokenState::_None() &&
7046 _Task._GetImpl()->_M_pTokenState->_IsCanceled();
7047 if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled)
7048 {
7049 _Func();
7050 if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)
7051 {
7052 delete _PParam;
7053 }
7054 }
7055 else
7056 {
7057 _ASSERTE(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled);
7058 if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled)
7059 {
7060 if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder()))
7061 {
7062 // This can only enter once.
7063 _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState;
7064 _ASSERTE(_PParam->_M_exceptionRelatedToken);
7065 // Deref token will be done in the _PParam destructor.
7066 if (_PParam->_M_exceptionRelatedToken != _CancellationTokenState::_None())
7067 {
7068 _PParam->_M_exceptionRelatedToken->_Reference();
7069 }
7070 }
7071 }
7072
7073 if (atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks)
7074 {
7075 // If no one has be completed so far, we need to make some final cancellation decision.
7076 if (!_PParam->_M_Completed._IsTriggered())
7077 {
7078 // If we already explicit token, we can skip the token join part.
7079 if (!_PParam->_M_fHasExplicitToken)
7080 {
7081 if (_PParam->_M_exceptionRelatedToken)
7082 {
7083 _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken);
7084 }
7085 else
7086 {
7087 // If haven't captured any exception token yet, there was no exception for all those tasks,
7088 // so just pick a random token (current one) for normal cancellation.
7089 _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState);
7090 }
7091 }
7092 // Do exception cancellation or normal cancellation based on whether it has stored exception.
7093 _PParam->_M_Completed._Cancel();
7094 }
7095 delete _PParam;
7096 }
7097 }
7098 }
7099
7100 template<typename _ElementType, typename _Iterator>
7101 struct _WhenAnyImpl
7102 {
7103 static task<std::pair<_ElementType, size_t>> _Perform(const task_options& _TaskOptions,
7104 _Iterator _Begin,
7105 _Iterator _End)
7106 {
7107 if (_Begin == _End)
7108 {
7109 throw invalid_operation("when_any(begin, end) cannot be called on an empty container.");
7110 }
7111 _CancellationTokenState* _PTokenState =
7112 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
7113 auto _PParam = new _RunAnyParam<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*>>();
7114
7115 if (_PTokenState)
7116 {
7117 _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState);
7118 _PParam->_M_fHasExplicitToken = true;
7119 }
7120
7121 task_options _Options(_TaskOptions);
7122 _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token());
7123 task<std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*>> _Any_tasks_completed(
7124 _PParam->_M_Completed, _Options);
7125
7126 // Keep a copy ref to the token source
7127 auto _CancellationSource = _PParam->_M_cancellationSource;
7128
7129 _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End));
7130 size_t _Index = 0;
7131 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
7132 {
7133 if (_PTask->is_apartment_aware())
7134 {
7135 _Any_tasks_completed._SetAsync();
7136 }
7137
7138 _PTask->_Then(
7139 [_PParam, _Index](task<_ElementType> _ResultTask) {
7140 auto _PParamCopy = _PParam; // Dev10
7141 auto _IndexCopy = _Index; // Dev10
7142 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() {
7143 _PParamCopy->_M_Completed.set(
7144 std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy),
7145 _ResultTask._GetImpl()->_M_pTokenState));
7146 };
7147
7148 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7149 },
7150 _CancellationTokenState::_None());
7151
7152 _Index++;
7153 }
7154
7155 // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created.
7156 return _Any_tasks_completed._Then(
7157 [=](std::pair<std::pair<_ElementType, size_t>, _CancellationTokenState*> _Result)
7158 -> std::pair<_ElementType, size_t> {
7159 _ASSERTE(_Result.second);
7160 if (!_PTokenState)
7161 {
7162 _JoinAllTokens_Add(_CancellationSource, _Result.second);
7163 }
7164 return _Result.first;
7165 },
7166 nullptr);
7167 }
7168 };
7169
7170 template<typename _Iterator>
7171 struct _WhenAnyImpl<void, _Iterator>
7172 {
7173 static task<size_t> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
7174 {
7175 if (_Begin == _End)
7176 {
7177 throw invalid_operation("when_any(begin, end) cannot be called on an empty container.");
7178 }
7179
7180 _CancellationTokenState* _PTokenState =
7181 _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr;
7182 auto _PParam = new _RunAnyParam<std::pair<size_t, _CancellationTokenState*>>();
7183
7184 if (_PTokenState)
7185 {
7186 _JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState);
7187 _PParam->_M_fHasExplicitToken = true;
7188 }
7189
7190 task_options _Options(_TaskOptions);
7191 _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token());
7192 task<std::pair<size_t, _CancellationTokenState*>> _Any_tasks_completed(_PParam->_M_Completed, _Options);
7193
7194 // Keep a copy ref to the token source
7195 auto _CancellationSource = _PParam->_M_cancellationSource;
7196
7197 _PParam->_M_numTasks = static_cast<size_t>(std::distance(_Begin, _End));
7198 size_t _Index = 0;
7199 for (auto _PTask = _Begin; _PTask != _End; ++_PTask)
7200 {
7201 if (_PTask->is_apartment_aware())
7202 {
7203 _Any_tasks_completed._SetAsync();
7204 }
7205
7206 _PTask->_Then(
7207 [_PParam, _Index](task<void> _ResultTask) {
7208 auto _PParamCopy = _PParam; // Dev10
7209 auto _IndexCopy = _Index; // Dev10
7210 auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() {
7211 _PParamCopy->_M_Completed.set(
7212 std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState));
7213 };
7214 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7215 },
7216 _CancellationTokenState::_None());
7217
7218 _Index++;
7219 }
7220
7221 // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created.
7222 return _Any_tasks_completed._Then(
7223 [=](std::pair<size_t, _CancellationTokenState*> _Result) -> size_t {
7224 _ASSERTE(_Result.second);
7225 if (!_PTokenState)
7226 {
7227 _JoinAllTokens_Add(_CancellationSource, _Result.second);
7228 }
7229 return _Result.first;
7230 },
7231 nullptr);
7232 }
7233 };
7234 } // namespace details
7235
7236 /// <summary>
7237 /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes
7238 /// successfully.
7239 /// </summary>
7240 /// <typeparam name="_Iterator">
7241 /// The type of the input iterator.
7242 /// </typeparam>
7243 /// <param name="_Begin">
7244 /// The position of the first element in the range of elements to be combined into the resulting task.
7245 /// </param>
7246 /// <param name="_End">
7247 /// The position of the first element beyond the range of elements to be combined into the resulting task.
7248 /// </param>
7249 /// <returns>
7250 /// A task that completes successfully when any one of the input tasks has completed successfully. If the input
7251 /// tasks are of type <c>T</c>, the output of this function will be a <c>task<std::pair<T,
7252 /// size_t>>></c>, where the first element of the pair is the result of the completing task, and the second
7253 /// element is the index of the task that finished. If the input tasks are of type <c>void</c> the output is a
7254 /// <c>task<size_t></c>, where the result is the index of the completing task.
7255 /// </returns>
7256 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7257 /**/
7258 template<typename _Iterator>
7259 auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())
7260 -> decltype(details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,
7261 _Iterator>::_Perform(_TaskOptions, _Begin, _End))
7262 {
7263 typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;
7264 return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End);
7265 }
7266
7267 /// <summary>
7268 /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes
7269 /// successfully.
7270 /// </summary>
7271 /// <typeparam name="_Iterator">
7272 /// The type of the input iterator.
7273 /// </typeparam>
7274 /// <param name="_Begin">
7275 /// The position of the first element in the range of elements to be combined into the resulting task.
7276 /// </param>
7277 /// <param name="_End">
7278 /// The position of the first element beyond the range of elements to be combined into the resulting task.
7279 /// </param>
7280 /// <param name="_CancellationToken">
7281 /// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation
7282 /// token, the resulting task will receive the cancellation token of the task that causes it to complete.
7283 /// </param>
7284 /// <returns>
7285 /// A task that completes successfully when any one of the input tasks has completed successfully. If the input
7286 /// tasks are of type <c>T</c>, the output of this function will be a <c>task<std::pair<T,
7287 /// size_t>>></c>, where the first element of the pair is the result of the completing task, and the second
7288 /// element is the index of the task that finished. If the input tasks are of type <c>void</c> the output is a
7289 /// <c>task<size_t></c>, where the result is the index of the completing task.
7290 /// </returns>
7291 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7292 /**/
7293 template<typename _Iterator>
7294 auto when_any(_Iterator _Begin, _Iterator _End, cancellation_token _CancellationToken)
7295 -> decltype(details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::value_type::result_type,
7296 _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End))
7297 {
7298 typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType;
7299 return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End);
7300 }
7301
7302 /// <summary>
7303 /// Creates a task that will complete successfully when either of the tasks supplied as arguments completes
7304 /// successfully.
7305 /// </summary>
7306 /// <typeparam name="_ReturnType">
7307 /// The type of the returned task.
7308 /// </typeparam>
7309 /// <param name="_Lhs">
7310 /// The first task to combine into the resulting task.
7311 /// </param>
7312 /// <param name="_Rhs">
7313 /// The second task to combine into the resulting task.
7314 /// </param>
7315 /// <returns>
7316 /// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks
7317 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T></c>. If the input
7318 /// tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for a
7319 /// construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking
7320 /// precedence over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of
7321 /// type <c>task<std::vector<T>></c> and the other one is of type <c>task<T>.</c></para>
7322 /// </returns>
7323 /// <remarks>
7324 /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,
7325 /// and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on
7326 /// that task.
7327 /// </remarks>
7328 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7329 /**/
7330 template<typename _ReturnType>
7331 task<_ReturnType> operator||(const task<_ReturnType>& _Lhs, const task<_ReturnType>& _Rhs)
7332 {
7333 auto _PParam = new details::_RunAnyParam<std::pair<_ReturnType, size_t>>();
7334
7335 task<std::pair<_ReturnType, size_t>> _Any_tasks_completed(_PParam->_M_Completed,
7336 _PParam->_M_cancellationSource.get_token());
7337 // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,
7338 // So that _PParam can be used before it getting deleted.
7339 auto _ReturnTask = _Any_tasks_completed._Then(
7340 [=](std::pair<_ReturnType, size_t> _Ret) -> _ReturnType {
7341 _ASSERTE(_Ret.second);
7342 _JoinAllTokens_Add(_PParam->_M_cancellationSource,
7343 reinterpret_cast<details::_CancellationTokenState*>(_Ret.second));
7344 return _Ret.first;
7345 },
7346 nullptr);
7347
7348 if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())
7349 {
7350 _ReturnTask._SetAsync();
7351 }
7352
7353 _PParam->_M_numTasks = 2;
7354 auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) {
7355 // Dev10 compiler bug
7356 auto _PParamCopy = _PParam;
7357 auto _Func = [&_ResultTask, _PParamCopy]() {
7358 _PParamCopy->_M_Completed.set(
7359 std::make_pair(_ResultTask._GetImpl()->_GetResult(),
7360 reinterpret_cast<size_t>(_ResultTask._GetImpl()->_M_pTokenState)));
7361 };
7362 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7363 };
7364
7365 _Lhs._Then(_Continuation, details::_CancellationTokenState::_None());
7366 _Rhs._Then(_Continuation, details::_CancellationTokenState::_None());
7367
7368 return _ReturnTask;
7369 }
7370
7371 /// <summary>
7372 /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes
7373 /// successfully.
7374 /// </summary>
7375 /// <typeparam name="_ReturnType">
7376 /// The type of the returned task.
7377 /// </typeparam>
7378 /// <param name="_Lhs">
7379 /// The first task to combine into the resulting task.
7380 /// </param>
7381 /// <param name="_Rhs">
7382 /// The second task to combine into the resulting task.
7383 /// </param>
7384 /// <returns>
7385 /// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks
7386 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T></c>. If the input
7387 /// tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for a
7388 /// construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking
7389 /// precedence over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of
7390 /// type <c>task<std::vector<T>></c> and the other one is of type <c>task<T>.</c></para>
7391 /// </returns>
7392 /// <remarks>
7393 /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,
7394 /// and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on
7395 /// that task.
7396 /// </remarks>
7397 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7398 /**/
7399 template<typename _ReturnType>
7400 task<std::vector<_ReturnType>> operator||(const task<std::vector<_ReturnType>>& _Lhs, const task<_ReturnType>& _Rhs)
7401 {
7402 auto _PParam = new details::_RunAnyParam<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*>>();
7403
7404 task<std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*>> _Any_tasks_completed(
7405 _PParam->_M_Completed, _PParam->_M_cancellationSource.get_token());
7406
7407 // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,
7408 // So that _PParam can be used before it getting deleted.
7409 auto _ReturnTask = _Any_tasks_completed._Then(
7410 [=](std::pair<std::vector<_ReturnType>, details::_CancellationTokenState*> _Ret) -> std::vector<_ReturnType> {
7411 _ASSERTE(_Ret.second);
7412 _JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second);
7413 return _Ret.first;
7414 },
7415 nullptr);
7416
7417 if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())
7418 {
7419 _ReturnTask._SetAsync();
7420 }
7421
7422 _PParam->_M_numTasks = 2;
7423 _Lhs._Then(
7424 [_PParam](task<std::vector<_ReturnType>> _ResultTask) {
7425 // Dev10 compiler bug
7426 auto _PParamCopy = _PParam;
7427 auto _Func = [&_ResultTask, _PParamCopy]() {
7428 auto _Result = _ResultTask._GetImpl()->_GetResult();
7429 _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState));
7430 };
7431 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7432 },
7433 details::_CancellationTokenState::_None());
7434
7435 _Rhs._Then(
7436 [_PParam](task<_ReturnType> _ResultTask) {
7437 auto _PParamCopy = _PParam;
7438 auto _Func = [&_ResultTask, _PParamCopy]() {
7439 auto _Result = _ResultTask._GetImpl()->_GetResult();
7440
7441 std::vector<_ReturnType> _Vec;
7442 _Vec.push_back(_Result);
7443 _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState));
7444 };
7445 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7446 },
7447 details::_CancellationTokenState::_None());
7448
7449 return _ReturnTask;
7450 }
7451
7452 /// <summary>
7453 /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes
7454 /// successfully.
7455 /// </summary>
7456 /// <typeparam name="_ReturnType">
7457 /// The type of the returned task.
7458 /// </typeparam>
7459 /// <param name="_Lhs">
7460 /// The first task to combine into the resulting task.
7461 /// </param>
7462 /// <param name="_Rhs">
7463 /// The second task to combine into the resulting task.
7464 /// </param>
7465 /// <returns>
7466 /// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks
7467 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T></c>. If the input
7468 /// tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for a
7469 /// construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking
7470 /// precedence over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of
7471 /// type <c>task<std::vector<T>></c> and the other one is of type <c>task<T>.</c></para>
7472 /// </returns>
7473 /// <remarks>
7474 /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,
7475 /// and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on
7476 /// that task.
7477 /// </remarks>
7478 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7479 /**/
7480 template<typename _ReturnType>
7481 auto operator||(const task<_ReturnType>& _Lhs, const task<std::vector<_ReturnType>>& _Rhs) -> decltype(_Rhs || _Lhs)
7482 {
7483 return _Rhs || _Lhs;
7484 }
7485
7486 /// <summary>
7487 /// Creates a task that will complete successfully when any of the tasks supplied as arguments completes
7488 /// successfully.
7489 /// </summary>
7490 /// <typeparam name="_ReturnType">
7491 /// The type of the returned task.
7492 /// </typeparam>
7493 /// <param name="_Lhs">
7494 /// The first task to combine into the resulting task.
7495 /// </param>
7496 /// <param name="_Rhs">
7497 /// The second task to combine into the resulting task.
7498 /// </param>
7499 /// <returns>
7500 /// A task that completes successfully when either of the input tasks has completed successfully. If the input tasks
7501 /// are of type <c>T</c>, the output of this function will be a <c>task<std::vector<T></c>. If the input
7502 /// tasks are of type <c>void</c> the output task will also be a <c>task<void></c>. <para> To allow for a
7503 /// construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking
7504 /// precedence over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of
7505 /// type <c>task<std::vector<T>></c> and the other one is of type <c>task<T>.</c></para>
7506 /// </returns>
7507 /// <remarks>
7508 /// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state,
7509 /// and one of the exceptions, if any are encountered, will be thrown when you call <c>get()</c> or <c>wait()</c> on
7510 /// that task.
7511 /// </remarks>
7512 /// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
7513 /**/
7514 template<typename _Ty = task<void>, typename _Pair = std::pair<details::_Unit_type, details::_CancellationTokenState*>>
7515 _Ty operator||(const task<void>& _Lhs_arg, const task<void>& _Rhs_arg)
7516 {
7517 const _Ty& _Lhs = _Lhs_arg;
7518 const _Ty& _Rhs = _Rhs_arg;
7519 auto _PParam = new details::_RunAnyParam<_Pair>();
7520
7521 task<std::pair<details::_Unit_type, details::_CancellationTokenState*>> _Any_task_completed(
7522 _PParam->_M_Completed, _PParam->_M_cancellationSource.get_token());
7523 // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called,
7524 // So that _PParam can be used before it getting deleted.
7525 auto _ReturnTask = _Any_task_completed._Then(
7526 [=](_Pair _Ret) {
7527 _ASSERTE(_Ret.second);
7528 details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second);
7529 },
7530 nullptr);
7531
7532 if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware())
7533 {
7534 _ReturnTask._SetAsync();
7535 }
7536
7537 _PParam->_M_numTasks = 2;
7538 auto _Continuation = [_PParam](_Ty _ResultTask) mutable {
7539 // Dev10 compiler needs this.
7540 auto _PParam1 = _PParam;
7541 auto _Func = [&_ResultTask, _PParam1]() {
7542 _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState));
7543 };
7544 _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask);
7545 };
7546
7547 _Lhs._Then(_Continuation, details::_CancellationTokenState::_None());
7548 _Rhs._Then(_Continuation, details::_CancellationTokenState::_None());
7549
7550 return _ReturnTask;
7551 }
7552
7553 template<typename _Ty>
7554 task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options())
7555 {
7556 task_completion_event<_Ty> _Tce;
7557 _Tce.set(_Param);
7558 return create_task(_Tce, _TaskOptions);
7559 }
7560
7561 template<class _Ty = void>
7562 inline task<_Ty> task_from_result(const task_options& _TaskOptions = task_options())
7563 {
7564 task_completion_event<_Ty> _Tce;
7565 _Tce.set();
7566 return create_task(_Tce, _TaskOptions);
7567 }
7568
7569 template<typename _TaskType, typename _ExType>
7570 task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options())
7571 {
7572 task_completion_event<_TaskType> _Tce;
7573 _Tce.set_exception(_Exception);
7574 return create_task(_Tce, _TaskOptions);
7575 }
7576
7577 } // namespace pplx
7578
7579 #pragma pop_macro("new")
7580
7581 #if defined(_MSC_VER)
7582 #pragma warning(pop)
7583 #endif
7584 #pragma pack(pop)
7585
7586 #endif // (defined(_MSC_VER) && (_MSC_VER >= 1800))
7587
7588 #ifndef _CONCRT_H
7589 #ifndef _LWRCASE_CNCRRNCY
7590 #define _LWRCASE_CNCRRNCY
7591 // Note to reader: we're using lower-case namespace names everywhere, but the 'Concurrency' namespace
7592 // is capitalized for historical reasons. The alias let's us pretend that style issue doesn't exist.
7593 namespace Concurrency
7594 {
7595 }
7596 namespace concurrency = Concurrency;
7597 #endif
7598 #endif
7599
7600 #endif // PPLXTASKS_H
7601